You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin开发NDEF标签写入:避免内容转换的技术问题

问题

开发一款Demo应用,实现读取NDEF标签记录并覆盖写入的功能。目前已实现readNdef、writeNdef和readTag三个核心函数,其中readNdef与readTag功能正常,但writeNdef函数存在异常:使用NdefRecord.createTextRecord方法写入记录时,内容会被自动转换,希望能够原样写入令牌内容。

相关代码实现

readNdef函数

private fun readNdef(tag: Tag?) {
        val ndef = Ndef.get(tag)
        try {
            ndef.connect()
            val inNdef = ndef.ndefMessage
            Log.d("fdsfsdfsdf", inNdef.toString())
            val inNdefRecords = ndef.ndefMessage.records
            Log.d("fdsfsdfsdf", inNdefRecords.toString())
            // convert the payload to string and drop 3 characters to get
            // rid of the " en" prefix
            val payload = inNdefRecords[0].payload
            Log.d("fdsfsdfsdf", payload.toString())

            // figure out if we need to take out the " en" at the beginning
            val textEncoding = if (payload[0] and 128.toByte() == 0.toByte()) "UTF-8" else "UTF-16"
            val langCodeLength = payload[0] and 63.toByte()

            // create a string starting by skipping the first 3 characters
            // based on the language code length
            var inMessage = String(
                payload,
                langCodeLength + 1,
                payload.count() - langCodeLength - 1,
                charset(textEncoding)
            )

            // try to convert the message to json
            try {
                val json = inMessage
                Log.d("fdsfsdfsdf", json)
                //val json = JsonParser().parse(inMessage)

                // ... use json or whatever here
            } catch (error: Exception) {
                Log.d(
                    "fdsfsdfsdf",
                    "NFC tag data seems to invalid:\n\n$inMessage\n\n${error.localizedMessage}"
                )
            }

            // ... do whatever
        } catch (error: Exception) {
            Log.d("fdsfsdfsdf", "Error attempting to pull tag info: ${error.localizedMessage}")
        } finally {
            ndef.close()
        }
    }

readTag函数

fun readTag(tag: Tag?) {
        Coroutines.default(this@MainViewModel) {
            readNdef(tag)
            Log.d(TAG, "readTag(${tag} ${tag?.techList})")
            postNFCStatus(NFCStatus.Process)
            val stringBuilder: StringBuilder = StringBuilder()
            val id: ByteArray? = tag?.id
            Log.d("fdsfsdfsdf", "$id")
            stringBuilder.append("Tag ID (hex): ${bytesToHex(id!!)} \n")
            stringBuilder.append("Tag ID (dec): ${getDec(id)} \n")
            stringBuilder.append("Tag ID (reversed): ${getReversed(id)} \n")
            stringBuilder.append("Technologies: ")
            tag.techList.forEach { tech ->
                stringBuilder.append(tech.substring(prefix.length))
                stringBuilder.append(", ")
            }
            stringBuilder.delete(stringBuilder.length - 2, stringBuilder.length)
            tag.techList.forEach { tech ->
                if (tech.equals(MifareClassic::class.java.name)) {
                    stringBuilder.append('\n')
                    val mifareTag: MifareClassic = MifareClassic.get(tag)
                    val type: String
                    if (mifareTag.type == MifareClassic.TYPE_CLASSIC) type = "Classic"
                    else if (mifareTag.type == MifareClassic.TYPE_PLUS) type = "Plus"
                    else if (mifareTag.type == MifareClassic.TYPE_PRO) type = "Pro"
                    else type = "Unknown"
                    stringBuilder.append("Mifare Classic type: $type \n")
                    stringBuilder.append("Mifare size: ${mifareTag.size} bytes \n")
                    stringBuilder.append("Mifare sectors: ${mifareTag.sectorCount} \n")
                    stringBuilder.append("Mifare blocks: ${mifareTag.blockCount}")
                }
                if (tech.equals(MifareUltralight::class.java.name)) {
                    stringBuilder.append('\n');
                    val mifareUlTag: MifareUltralight = MifareUltralight.get(tag);
                    val type: String
                    if (mifareUlTag.type == MifareUltralight.TYPE_ULTRALIGHT) type =
                        "Ultralight"
                    else if (mifareUlTag.type == MifareUltralight.TYPE_ULTRALIGHT_C) type =
                        "Ultralight C"
                    else type = "Unkown"
                    stringBuilder.append("Mifare Ultralight type: ");
                    stringBuilder.append(type)
                }
            }
            Log.d(TAG, "Datum: $stringBuilder")
            Log.d(ContentValues.TAG, "dumpTagData Return \n $stringBuilder")
            postNFCStatus(NFCStatus.Read)
            liveTag.emit("${getDateTimeNow()} \n $stringBuilder")
        }
    }

writeNdef函数

fun writeNdef(tag: Tag?, context: Context) {
        val ndef = Ndef.get(tag)
        try {
            ndef.connect()
            val textRecord1 = NdefRecord.createTextRecord(
                "en",  // language code (ISO 639-1)
                "00000111111222222333333444444555555AABBEF"
                      //  context.getString(R.string.ndef_rcrd1) // text to send
            )
            val textRecord2 = NdefRecord.createTextRecord(
                "en",  // language code (ISO 639-1)
                "00000111111222222333333444444555555AABBEF"
                      //  context.getString(R.string.ndef_rcrd2) // text to send
            )
            val textRecord3 = NdefRecord.createTextRecord(
                "en",  // language code (ISO 639-1)
                "00000111111222222333333444444555555AABBEF"
                       // context.getString(R.string.ndef_rcrd3) // text to send
            )
            val textRecord4 = NdefRecord.createTextRecord(
                "en",  // language code (ISO 639-1)
                "00000111111222222333333444444555555AABBEF"
                        //context.getString(R.string.ndef_rcrd4) // text to send
            )
            val ndefMessage = NdefMessage(textRecord1, textRecord2, textRecord3, textRecord4)
            ndef.writeNdefMessage(ndefMessage)
            Coroutines.default(this@MainViewModel){
                postNFCStatus(NFCStatus.Write)
            }
            Log.d("fdsfsdfsdf","Edited: ${ndef.ndefMessage}")
        } catch (e: Exception) {
            Log.d("fdsfsdfsdf", "Error write: ${e.localizedMessage}")
        } finally {
            ndef.close()
        }
    }
解决方案

NdefRecord.createTextRecord是专门用于创建NDEF文本类型记录的方法,它会自动在payload头部添加语言编码标识和字符编码信息(比如你读取时跳过的"en"前缀相关字节),导致原始令牌内容被包装转换。要实现无转换的原样写入,需要直接构造自定义NDEF记录,跳过文本格式的自动包装。

修改后的writeNdef函数

fun writeNdef(tag: Tag?, context: Context) {
    val ndef = Ndef.get(tag)
    try {
        ndef.connect()
        // 原始令牌内容
        val tokenContent = "00000111111222222333333444444555555AABBEF"
        // 将字符串转为UTF-8字节数组,作为原始payload
        val payload = tokenContent.toByteArray(Charsets.UTF_8)
        
        // 构造自定义NDEF记录:使用MIME类型标识纯文本,避免自动格式转换
        val customRecord = NdefRecord(
            NdefRecord.TNF_MIME_MEDIA,  // 类型名称格式:MIME媒体类型
            "text/plain".toByteArray(), // MIME类型
            ByteArray(0),               // 记录ID(空即可)
            payload                     // 原始内容字节数组
        )
        
        // 创建包含4个相同记录的NDEF消息
        val ndefMessage = NdefMessage(customRecord, customRecord, customRecord, customRecord)
        ndef.writeNdefMessage(ndefMessage)
        
        Coroutines.default(this@MainViewModel){
            postNFCStatus(NFCStatus.Write)
        }
        Log.d("fdsfsdfsdf","Edited: ${ndef.ndefMessage}")
    } catch (e: Exception) {
        Log.d("fdsfsdfsdf", "Error write: ${e.localizedMessage}")
    } finally {
        ndef.close()
    }
}

对应读取逻辑调整

如果使用上述方式写入,读取时无需再跳过语言编码前缀,直接将payload转为字符串即可:

// 在readNdef函数中替换原有的inMessage生成逻辑
val inMessage = String(inNdefRecords[0].payload, Charsets.UTF_8)

说明

  • 如果你需要写入的是二进制令牌(而非字符串),直接传入对应的ByteArray作为payload即可。
  • 也可以根据需求选择其他TNF类型,比如TNF_ABSOLUTE_URI(用于URI记录)或TNF_UNKNOWN(通用未知类型),只要不使用createTextRecord就不会触发自动格式转换。

内容的提问来源于stack exchange,提问作者Tural Sufan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 22:16:00