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 设备主要分为三类:
- 读卡器模式(Reader/Writer Mode):设备可以读取或写入 NFC 标签的数据,例如公交卡读卡器、门禁读卡器等。
- 卡模拟模式(Card Emulation Mode):设备模拟成一张 NFC 卡,如手机模拟公交卡进行刷卡支付。
- 点对点模式(Peer - to - Peer Mode):两个 NFC 设备之间直接进行数据交换,比如两个手机之间传输联系人信息。
二、Kotlin 开发环境准备
在进行 Kotlin NFC 开发之前,需要确保开发环境已正确配置。
- IDE 选择:推荐使用 Android Studio,它是 Android 开发的官方 IDE,对 Kotlin 有良好的支持。可以从官网下载并安装最新版本。
- 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'
- 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 标签
- 初始化 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()
}
}
- 处理 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
}
- 读取其他类型标签:除了 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 标签
- 写入 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()
}
}
- 写入其他类型标签:对于 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 卡模拟模式开发
- 配置卡模拟权限:在 AndroidManifest.xml 文件中添加以下权限:
<uses - permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
- 创建 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) {
// 服务停用处理
}
}
- 注册服务:在 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 点对点模式开发
- 初始化点对点模式:在 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)
}
- 接收数据:在接收端 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 开发中的常见问题及解决方法
- 权限问题:如果在运行时出现权限相关错误,确保在 AndroidManifest.xml 文件中正确配置了 NFC 权限,并且对于一些特殊权限(如卡模拟所需的
WRITE_SECURE_SETTINGS
),需要按照系统要求进行申请。在 Android 6.0 及以上版本,还需要动态申请一些敏感权限。 - 标签兼容性问题:不同类型和厂家的 NFC 标签可能存在兼容性差异。在开发过程中,要尽可能使用多种类型的标签进行测试。对于不支持 NDEF 格式的标签,需要根据其具体类型(如 Mifare Classic)使用相应的库和方法进行操作。
- 性能问题:在进行大量数据读写或频繁的 NFC 操作时,可能会出现性能问题。可以优化代码逻辑,例如批量读取或写入数据,减少不必要的连接和断开操作。同时,合理设置超时时间,避免长时间等待造成的卡顿。
- 安全问题:NFC 涉及数据传输和敏感信息处理,安全至关重要。在卡模拟模式下,要对传输的 APDU 命令进行严格的验证和加密处理。对于读取和写入的数据,要防止数据泄露和篡改,可以采用加密算法(如 AES)对数据进行保护。
八、NFC 与其他技术的结合应用
- NFC 与蓝牙:NFC 可以用于快速配对蓝牙设备。例如,两个支持 NFC 的设备靠近时,通过 NFC 交换蓝牙配对信息,然后快速建立蓝牙连接,进行大数据传输。在 Kotlin 中,可以先通过 NFC 获取对方设备的蓝牙地址等信息,然后利用 Android 的蓝牙 API 进行连接。
- NFC 与二维码:NFC 和二维码都可以用于数据传输。可以结合两者的优势,例如在一些场景下,使用 NFC 进行快速启动和初步数据交换,然后通过二维码提供更详细的信息或下载链接。在应用中,可以通过 Kotlin 生成二维码,并利用 NFC 标签存储二维码的相关信息。
- NFC 与物联网:在物联网环境中,NFC 可以作为设备的身份验证和配置手段。例如,新的物联网设备接入网络时,可以通过 NFC 与手机等设备进行交互,手机将网络配置信息写入 NFC 标签,设备读取标签后自动完成配置。同时,NFC 还可以用于设备之间的近距离通信和数据共享。
通过以上详细的介绍和代码示例,希望能帮助开发者全面掌握 Kotlin 环境下的 NFC 近场通信开发技术,在实际应用开发中灵活运用,创造出更多创新的 NFC 应用场景。