我的应用程序使用前台调度系统允许用户点击他们的NFC标签,以便对标签执行读写操作。
如果用户正确点击他们的标签(即,他们在手机上的正确位置点击它并将其连接足够长时间),它可以很好地工作,但如果他们过早地物理删除标签,那么{{1}抛出IOException。
这意味着写操作失败,这是公平的。但真正的问题是同样失败的操作也从标签中删除整个ndef格式/消息!
我的代码是围绕Advanced NFC | Android Developers页面的代码段构建的(不幸的是,link to the ForegroundDispatch sample似乎已被破坏,并且没有可导入Android Studio的示例项目。)
步骤1。当用户首次点击他们的NFC标签时,这是logcat / stacktrace输出,但过早地将其移走:
ndef.writeNdefMessage(...)
第2步。接下来,同一个用户再次点击相同的标记,但它似乎不再包含ndef消息(我已通过更改代码并检查03-28 20:15:18.589 21278-21278/com.example.exampleapp E/NfcTestActivity: Tag error
java.io.IOException
at android.nfc.tech.Ndef.writeNdefMessage(Ndef.java:320)
at com.example.exampleapp.NfcTestActivity.onNewIntent(NfcTestActivity.java:170)
at android.app.Instrumentation.callActivityOnNewIntent(Instrumentation.java:1224)
at android.app.ActivityThread.deliverNewIntents(ActivityThread.java:2946)
at android.app.ActivityThread.performNewIntents(ActivityThread.java:2959)
at android.app.ActivityThread.handleNewIntent(ActivityThread.java:2968)
at android.app.ActivityThread.access$1700(ActivityThread.java:181)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1554)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6145)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
03-28 20:15:18.599 1481-17792/? E/SecNfcJni: nfaConnectionCallback: NFA_SELECT_RESULT_EVT error: status = 3
03-28 20:15:18.599 1481-1502/? E/SecNfcJni: reSelect: tag is not active
确认了该消息返回ndef.getCachedNdefMessage()
):
null
到目前为止我使用的两款设备都遇到了这个问题 - 运行Android 5.1.1的三星Galaxy Core Prime (低端手机)和三星Galaxy A5 < / em>(中档手机)运行Android 5.0.2。
我的应用使用的NFC标签包含重要信息(即,无意中删除了标签数据不是一种选择!),所以我的问题是......
经过大量的搜索后,我很惊讶这个问题还没有在别处讨论过,所以如果问题与我的代码无关,那么可能与我正在使用的NFC标签有关?...
我使用的标签是 NXP MIFARE Ultralight(Ultralight C) - NTAG203 (标签类型:ISO 14443-3A)。其中一些我从ebay购买,有些是我从Rapid NFC(一家声誉良好的公司)购买的,但我似乎对所有这些都有这个问题。
以下是我完整的活动代码:
03-28 20:15:27.499 21278-21278/com.example.exampleapp E/NfcTestActivity: Tag error
java.lang.Exception: Tag was not ndef formatted: android.nfc.action.TECH_DISCOVERED
at com.example.exampleapp.NfcTestActivity.onNewIntent(NfcTestActivity.java:124)
at android.app.Instrumentation.callActivityOnNewIntent(Instrumentation.java:1224)
at android.app.ActivityThread.deliverNewIntents(ActivityThread.java:2946)
at android.app.ActivityThread.performNewIntents(ActivityThread.java:2959)
at android.app.ActivityThread.handleNewIntent(ActivityThread.java:2968)
at android.app.ActivityThread.access$1700(ActivityThread.java:181)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1554)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:6145)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1399)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
答案 0 :(得分:4)
我真的很想知道在覆盖数据的过程中删除存储设备时还会发生什么。
您的代码并非真正“删除”数据。它只是开始覆盖标签内存开头的数据,当你中断写入时,标签处于未定义状态。
NFC标签仅支持一次存储一条NDEF消息。因此,当您开始编写新的NDEF消息时,需要覆盖旧的NDEF消息。因此,
ndef.writeNdefMessage(ndefMessageNew);
将覆盖从第一个块开始的现有NDEF消息。对于NTAG203,MIFARE Ultralight和MIFARE Ultralight C(顺便说一下是三种不同的标签类型),第一个块将在块4附近。writeNdefMessage
然后将写入新的消息块,用于用新数据替换旧数据。
如果写入过程被中断(例如,通过从读取器字段中拉出标签),则仅写入新消息的一部分(并且旧消息的一部分可以保留在标签上)。由于旧消息和新消息都不完整,因此Android(与任何其他NDEF读取器一样)无法从标记中读取有效的NDEF消息,因此不会检测到任何NDEF消息。您的应用仍会检测到该代码,因为您还注册了TECH_DISCOVERED
意图(不要求代码包含虚拟NDEF消息)。
如果您的NDEF消息很长,您的用户实际上可以在写入时拉动标记,那么您可以对拉动本身做很多事情(除非指示用户不这样做)。 NFC标签也没有开箱即用的任何形式的拉保护。即在完全写入新的NDEF消息之前,目前没有可靠地存储旧NDEF消息的标签。
您可能要做的是在应用程序中存储旧的(或新的)NDEF消息(可能映射到标记ID),并让用户在失败后重新启动写入过程。不过,这需要用户合作。
这可能是另一种选择:不要将NDEF用于关键数据,而是使用特定于应用程序的内存布局(或者除了NDEF之外)。 NTAG / MIFARE Ultralight在ISO 14443-3A(NFC-A)之上设置了命令集,不支持ISO-DEP(ISO 14443-4)。因此,您可以使用NfcA
(或MifareUltralight
)使用低级命令直接读取/写入标记。您可以将标记内存分为两个部分,用于存储旧数据和新数据:
Block x: Flag indicating which section (1 or 2) contains the valid data Block x+1: First block of section 1 Block x+2: Second block of section 1 [...] Block x+m: Last block of section 1 Block x+m+1: First block of section 2 Block x+m+2: Second block of section 2 [...] Block x+2*m: Last block of section 2
其中x
是您的自定义内存结构的第一个块(您甚至可以在一些固定的NDEF消息之后启动该区域)并且m
是块中每个部分的长度(NTAG上的1个块) / MF Ultralight有4个字节)。
然后,您可以使用类似的内容来阅读和更新您的代码:
x
读取以找出哪个部分包含vaild(最新)数据 - &gt;第s
节。s
部分中的数据并将其用作当前数据。s
= 1:第0部分;如果s
= 0:部分1)。x
。低级读写命令如下所示:
READ:
byte[] result = nfcA.transceive(new byte[] {
(byte)0x30, // READ
(byte)(blockNumber & 0x0ff)
});
WRITE:
byte[] result = nfcA.transceive(new byte[] {
(byte)0xA2, // WRITE
(byte)(blockNumber & 0x0ff),
byte0, byte1, byte2, byte3
});