MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Kotlin NFC近场通信开发详解

2025-01-047.2k 阅读

一、NFC 简介

NFC(Near Field Communication)即近场通信技术,它是一种短距离的高频无线通信技术,允许电子设备之间进行非接触式点对点数据传输 ,在大约 10 厘米的距离内交换数据。NFC 技术结合了非接触式读卡器、非接触式卡和点对点功能,其应用场景广泛,例如移动支付、门禁系统、电子票务、文件传输等。

NFC 工作频率为 13.56MHz,数据传输速率有 106 Kbit/s、212 Kbit/s 或者 424 Kbit/s 三种。NFC 设备主要分为三类:

  1. 读卡器模式(Reader/Writer Mode):设备可以读取或写入 NFC 标签的数据,例如公交卡读卡器、门禁读卡器等。
  2. 卡模拟模式(Card Emulation Mode):设备模拟成一张 NFC 卡,如手机模拟公交卡进行刷卡支付。
  3. 点对点模式(Peer - to - Peer Mode):两个 NFC 设备之间直接进行数据交换,比如两个手机之间传输联系人信息。

二、Kotlin 开发环境准备

在进行 Kotlin NFC 开发之前,需要确保开发环境已正确配置。

  1. IDE 选择:推荐使用 Android Studio,它是 Android 开发的官方 IDE,对 Kotlin 有良好的支持。可以从官网下载并安装最新版本。
  2. Kotlin 支持:如果使用 Android Studio,默认已对 Kotlin 有一定支持。但为了确保使用最新特性,需在项目的 build.gradle 文件中配置 Kotlin 插件版本。例如:
buildscript {
    ext.kotlin_version = '1.6.21'
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin - gradle - plugin:$kotlin_version"
    }
}

同时,在 app 模块的 build.gradle 文件中应用 Kotlin 插件:

apply plugin: 'kotlin - android'
apply plugin: 'kotlin - android - extensions'
  1. NFC 权限配置:在 AndroidManifest.xml 文件中添加 NFC 权限:
<uses - permission android:name="android.permission.NFC" />

为了确保设备支持 NFC 功能,还可以添加以下代码来声明设备必须具备 NFC 硬件:

<uses - feature
    android:name="android.hardware.nfc"
    android:required="true" />

三、NFC 基本操作 - 读取 NFC 标签

  1. 初始化 NFC 适配器:在 Kotlin 代码中,首先需要获取 NFC 适配器实例。在 Activity 中,可以如下操作:
private lateinit var nfcAdapter: NfcAdapter

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    nfcAdapter = NfcAdapter.getDefaultAdapter(this)
    if (nfcAdapter == null) {
        Toast.makeText(this, "设备不支持 NFC", Toast.LENGTH_SHORT).show()
        finish()
    }
}
  1. 处理 NFC 意图:当设备检测到 NFC 标签时,系统会发送一个 NFC 意图。我们需要在 Activity 中注册接收这个意图,并处理标签数据。在 AndroidManifest.xml 文件中添加以下代码:
<intent - filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:mimeType="text/plain" />
</intent - filter>

在 Activity 中处理意图:

override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
        intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.let { rawMessages ->
            val messages = rawMessages.map { it as NdefMessage }
            val text = parseNdefMessages(messages)
            textView.text = text
        }
    }
}

private fun parseNdefMessages(messages: List<NdefMessage>): String {
    var text = ""
    messages.forEach { message ->
        message.records.forEach { record ->
            try {
                text += String(record.payload, Charset.forName("UTF - 8"))
            } catch (e: UnsupportedEncodingException) {
                e.printStackTrace()
            }
        }
    }
    return text
}
  1. 读取其他类型标签:除了 NDEF 格式的标签,还有其他类型如 Mifare Classic 等。读取 Mifare Classic 标签需要使用 MifareClassic 类。首先获取 Tag 对象:
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
if (tag!= null) {
    try {
        val mifareClassic = MifareClassic.get(tag)
        mifareClassic.connect()
        for (i in 0 until mifareClassic.sectorCount) {
            val isAuthenticated = mifareClassic.authenticateSectorWithKeyA(i, MifareClassic.KEY_DEFAULT)
            if (isAuthenticated) {
                val block = mifareClassic.readBlock(i * 4)
                // 处理读取到的块数据
            }
        }
        mifareClassic.close()
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

四、NFC 基本操作 - 写入 NFC 标签

  1. 写入 NDEF 标签:要写入 NDEF 标签,首先构建一个 NdefMessage。例如,写入一段文本:
private fun createTextNdefMessage(text: String): NdefMessage {
    val langBytes = Locale.getDefault().language.toByteArray(Charset.forName("UTF - 8"))
    val textBytes = text.toByteArray(Charset.forName("UTF - 8"))
    val record = NdefRecord(
        NdefRecord.TNF_WELL_KNOWN,
        NdefRecord.RTD_TEXT,
        byteArrayOf(0),
        langBytes + byteArrayOf(0x00) + textBytes
    )
    return NdefMessage(arrayOf(record))
}

然后获取 Tag 对象并写入数据:

val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
if (tag!= null) {
    try {
        val ndef = Ndef.get(tag)
        if (ndef!= null) {
            ndef.connect()
            if (!ndef.isWritable) {
                Toast.makeText(this, "标签不可写", Toast.LENGTH_SHORT).show()
                return
            }
            val message = createTextNdefMessage("Hello, NFC!")
            ndef.writeNdefMessage(message)
            Toast.makeText(this, "写入成功", Toast.LENGTH_SHORT).show()
            ndef.close()
        } else {
            Toast.makeText(this, "标签不支持 NDEF 格式", Toast.LENGTH_SHORT).show()
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }
}
  1. 写入其他类型标签:对于 Mifare Classic 标签,写入数据前需要先进行身份验证。例如,向特定块写入数据:
val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
if (tag!= null) {
    try {
        val mifareClassic = MifareClassic.get(tag)
        mifareClassic.connect()
        val sectorIndex = 0
        val isAuthenticated = mifareClassic.authenticateSectorWithKeyA(sectorIndex, MifareClassic.KEY_DEFAULT)
        if (isAuthenticated) {
            val blockIndex = sectorIndex * 4
            val data = "Some data".toByteArray(Charset.forName("UTF - 8"))
            mifareClassic.writeBlock(blockIndex, data)
        }
        mifareClassic.close()
    } catch (e: Exception) {
        e.printStackTrace()
    }
}

五、NFC 卡模拟模式开发

  1. 配置卡模拟权限:在 AndroidManifest.xml 文件中添加以下权限:
<uses - permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
  1. 创建 Host - Card - Emulation Service:继承 HostApduService 类,并重写相关方法。例如:
class MyHceService : HostApduService() {
    override fun processCommandApdu(commandApdu: ByteArray, extras: Bundle): ByteArray {
        // 处理 APDU 命令
        return "Response data".toByteArray(Charset.forName("UTF - 8"))
    }

    override fun onDeactivated(reason: Int) {
        // 服务停用处理
    }
}
  1. 注册服务:在 AndroidManifest.xml 文件中注册服务:
<service
    android:name=".MyHceService"
    android:exported="true"
    android:permission="android.permission.BIND_NFC_SERVICE">
    <intent - filter>
        <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" />
    </intent - filter>
    <meta - data
        android:name="android.nfc.cardemulation.host_apdu_service"
        android:resource="@xml/apduservice" />
</service>

res/xml/apduservice.xml 文件中配置 AID(应用标识符):

<host - apdu - service xmlns:android="http://schemas.android.com/apk/res/android"
    android:requireDeviceUnlock="false">
    <aid - group android:description="My AID Group">
        <aid android:name="A000000003000000" />
    </aid - group>
</host - apdu - service>

六、NFC 点对点模式开发

  1. 初始化点对点模式:在 Activity 中初始化 NFC 适配器,并开启点对点模式:
private lateinit var nfcAdapter: NfcAdapter
private lateinit var beamCallback: NfcAdapter.CreateBeamUrisCallback

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    nfcAdapter = NfcAdapter.getDefaultAdapter(this)
    if (nfcAdapter == null) {
        Toast.makeText(this, "设备不支持 NFC", Toast.LENGTH_SHORT).show()
        finish()
    }

    beamCallback = NfcAdapter.CreateBeamUrisCallback {
        // 返回要传输的 URI
        arrayOf(Uri.parse("content://path/to/data"))
    }
    nfcAdapter.setBeamPushUrisCallback(beamCallback, this)
}
  1. 接收数据:在接收端 Activity 中,通过处理 NFC 意图来接收传输的数据:
override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
        intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.let { rawMessages ->
            val messages = rawMessages.map { it as NdefMessage }
            val text = parseNdefMessages(messages)
            textView.text = text
        }
    }
}

七、NFC 开发中的常见问题及解决方法

  1. 权限问题:如果在运行时出现权限相关错误,确保在 AndroidManifest.xml 文件中正确配置了 NFC 权限,并且对于一些特殊权限(如卡模拟所需的 WRITE_SECURE_SETTINGS),需要按照系统要求进行申请。在 Android 6.0 及以上版本,还需要动态申请一些敏感权限。
  2. 标签兼容性问题:不同类型和厂家的 NFC 标签可能存在兼容性差异。在开发过程中,要尽可能使用多种类型的标签进行测试。对于不支持 NDEF 格式的标签,需要根据其具体类型(如 Mifare Classic)使用相应的库和方法进行操作。
  3. 性能问题:在进行大量数据读写或频繁的 NFC 操作时,可能会出现性能问题。可以优化代码逻辑,例如批量读取或写入数据,减少不必要的连接和断开操作。同时,合理设置超时时间,避免长时间等待造成的卡顿。
  4. 安全问题:NFC 涉及数据传输和敏感信息处理,安全至关重要。在卡模拟模式下,要对传输的 APDU 命令进行严格的验证和加密处理。对于读取和写入的数据,要防止数据泄露和篡改,可以采用加密算法(如 AES)对数据进行保护。

八、NFC 与其他技术的结合应用

  1. NFC 与蓝牙:NFC 可以用于快速配对蓝牙设备。例如,两个支持 NFC 的设备靠近时,通过 NFC 交换蓝牙配对信息,然后快速建立蓝牙连接,进行大数据传输。在 Kotlin 中,可以先通过 NFC 获取对方设备的蓝牙地址等信息,然后利用 Android 的蓝牙 API 进行连接。
  2. NFC 与二维码:NFC 和二维码都可以用于数据传输。可以结合两者的优势,例如在一些场景下,使用 NFC 进行快速启动和初步数据交换,然后通过二维码提供更详细的信息或下载链接。在应用中,可以通过 Kotlin 生成二维码,并利用 NFC 标签存储二维码的相关信息。
  3. NFC 与物联网:在物联网环境中,NFC 可以作为设备的身份验证和配置手段。例如,新的物联网设备接入网络时,可以通过 NFC 与手机等设备进行交互,手机将网络配置信息写入 NFC 标签,设备读取标签后自动完成配置。同时,NFC 还可以用于设备之间的近距离通信和数据共享。

通过以上详细的介绍和代码示例,希望能帮助开发者全面掌握 Kotlin 环境下的 NFC 近场通信开发技术,在实际应用开发中灵活运用,创造出更多创新的 NFC 应用场景。