- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我的应用使用前台调度系统允许用户轻触他们的 NFC 标签,以便对标签执行先读后写操作。
如果用户正确点击他们的标签(即,他们在手机上的正确位置点击它并保持连接足够长的时间),它会很好地工作,但如果他们过早地移除标签,那么 ndef .writeNdefMessage(...)
抛出 IOException。
这意味着写入操作失败,这很公平。但真正的问题是同样的失败操作也删除标签中的整个 ndef 格式/消息!
我的代码是围绕 Advanced NFC | Android Developers 中的 fragment 构建的页面(不幸的是 link to the ForegroundDispatch sample 似乎已损坏,并且没有此类示例项目可导入到 Android Studio 中)。
第 1 步。这是用户首次轻触他们的 NFC 标签但过早将其移开时的 logcat/stacktrace 输出:
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
第 2 步。 接下来,同一个用户再次点击同一个标签,但它似乎不再包含 ndef 消息(我已通过更改代码并检查 ndef. getCachedNdefMessage()
返回 null
):
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)
到目前为止我测试过的两台设备都遇到了这个问题 - 运行 Android 5.1.1 的 Samsung Galaxy Core Prime(低端手机)和 Samsung Galaxy A5(中档手机)运行 Android 5.0.2。
我的应用程序使用的 NFC 标签包含重要信息(即,无意中删除标签数据不是一种选择!),所以我的问题是......
经过大量搜索后,我很惊讶这个问题没有在其他地方讨论过,所以如果问题不是与我的代码有关,那么是否与我使用的 NFC 标签有关?...
我使用的标签是 NXP MIFARE Ultralight (Ultralight C) - NTAG203(标签类型:ISO 14443-3A)。其中一些是我从 ebay 购买的,一些是我从 Rapid NFC(一家信誉良好的公司)购买的,但我似乎对所有这些都有这个问题。
这是我的 Activity 的完整代码:
package com.example.exampleapp;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;
public class NfcTestActivity extends AppCompatActivity {
private static String LOG_TAG = NfcTestActivity.class.getSimpleName();
private static int SUCCESS_COUNT = 0;
private static int FAILURE_COUNT = 0;
private NfcAdapter nfcAdapter;
private PendingIntent pendingIntent;
private IntentFilter[] intentFiltersArray;
private String[][] techListsArray;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nfc_test);
getSupportActionBar().setDisplayShowHomeEnabled(true);
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) {
makeToast("NFC not available!", Toast.LENGTH_LONG);
finish();
}
else {
//makeToast("NFC available");
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
try {
ndef.addDataType("*/*"); /* Handles all MIME based dispatches.
You should specify only the ones that you need. */
} catch (IntentFilter.MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
intentFiltersArray = new IntentFilter[]{
ndef
};
techListsArray = new String[][]{
new String[]{
Ndef.class.getName()
}
};
}
}
@Override
public void onPause() {
super.onPause();
if (nfcAdapter != null) {
nfcAdapter.disableForegroundDispatch(this);
}
}
@Override
public void onResume() {
super.onResume();
if (nfcAdapter != null) {
nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray);
}
}
public void onNewIntent(Intent intent) {
Ndef ndef = null;
try {
String action = intent.getAction();
//makeToast("action: " + action);
if (!NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
throw new Exception("Tag was not ndef formatted: " + action); // line #124
}
else {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//do something with tagFromIntent
ndef = Ndef.get(tag);
//makeToast("ndef: " + ndef);
if (ndef == null) {
throw new Exception("ndef == null!");
}
else {
// Connect
ndef.connect();
// Get cached message
NdefMessage ndefMessageOld = ndef.getCachedNdefMessage();
if (ndefMessageOld == null) {
throw new Exception("No ndef message on tag!");
}
else {
// Get old records
NdefRecord[] ndefRecordsOld = ndefMessageOld.getRecords();
int numRecords = (ndefRecordsOld == null) ? 0 : ndefRecordsOld.length;
// Create/copy 'new' records
NdefRecord[] ndefRecordsNew = new NdefRecord[numRecords];
for (int i = 0; i < numRecords; i++) {
ndefRecordsNew[i] = ndefRecordsOld[i];
}
// Create new message
NdefMessage ndefMessageNew = new NdefMessage(ndefRecordsNew);
// Write new message
ndef.writeNdefMessage(ndefMessageNew); // line #170
SUCCESS_COUNT++;
// Report success
String msg = "Read & wrote " + numRecords + " records.";
makeToast(msg);
Log.d(LOG_TAG, msg);
}
}
}
}
catch(Exception e) {
FAILURE_COUNT++;
Log.e(LOG_TAG, "Tag error", e);
makeToast("Tag error: " + e, Toast.LENGTH_LONG);
}
finally {
try {
if (ndef != null) {
ndef.close();
}
}
catch(Exception e) {
Log.e(LOG_TAG, "Error closing ndef", e);
makeToast("Error closing ndef: " + e, Toast.LENGTH_LONG);
}
makeToast("Successes: " + SUCCESS_COUNT + ". Failures: " + FAILURE_COUNT);
}
}
private void makeToast(final String msg) {
makeToast(msg, Toast.LENGTH_SHORT);
}
private void makeToast(final String msg, final int duration) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast.makeText(NfcTestActivity.this, msg, duration).show();
}
});
}
}
最佳答案
我真的很想知道,当您在覆盖数据的过程中移除存储设备时,您还会期望发生什么。
您的代码并不是真正的“删除”数据。当您中断写入时,它只是从标签内存的开头开始覆盖数据,使标签处于未定义状态。
一个NFC标签一次只支持存储一条NDEF消息。因此,当您开始编写新的 NDEF 消息时,需要覆盖旧的 NDEF 消息。因此,
ndef.writeNdefMessage(ndefMessageNew);
将从第一个 block 开始覆盖现有的 NDEF 消息。对于 NTAG203、MIFARE Ultralight 和 MIFARE Ultralight C(顺便说一下,这是三种不同的标签类型),第一个 block 将在 block 4 附近。writeNdefMessage
将写入新的消息 block ,用于 block 替换旧数据新数据。
如果写入过程被中断(例如,通过从读取器字段中拉出标签),则只会写入部分新消息(而部分旧消息可能会保留在标签上)。由于旧消息和新消息都不完整,Android(就像任何其他 NDEF 阅读器一样)无法从标签中读取有效的 NDEF 消息,因此不会检测到任何 NDEF 消息。该标签仍会被您的应用检测到,因为您还注册了 TECH_DISCOVERED
Intent (不需要标签包含有效的 NDEF 消息)。
如果您的 NDEF 消息太长以至于您的用户实际上能够在写入时拉取标签,那么您对拉取本身无能为力(除了指示用户不要这样做)。 NFC 标签也没有任何形式的开箱即用保护。 IE。目前没有标签可以可靠地存储旧的 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 dataBlock x+1: First block of section 1Block x+2: Second block of section 1[...]Block x+m: Last block of section 1Block x+m+1: First block of section 2Block x+m+2: Second block of section 2[...]Block x+2*m: Last block of section 2
Where x
is the first block of your custom memory structure (you could even start that area after some fixed NDEF message) and m
is the length of each section in blocks (1 block on NTAG/MF Ultralight has 4 bytes).
You would then use something like this to read and update your tag:
x
to find out which section contains the vaild (newest) data -> section s
.s
and use it as current data.s
= 1: section 0; if s
= 0: section 1).x
with the new section number.Low-level read and write commands look like this:
READ:
byte[] result = nfcA.transceive(new byte[] {
(byte)0x30, // READ
(byte)(blockNumber & 0x0ff)
});
写:
byte[] result = nfcA.transceive(new byte[] {
(byte)0xA2, // WRITE
(byte)(blockNumber & 0x0ff),
byte0, byte1, byte2, byte3
});
关于Android NFC - ndef.writeNdefMessage() 抛出 IOException 并删除标签数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36270238/
所以我使用一个带有整个 block 的标签作为链接,它是一个产品展示,所以你点击它会转到产品页面。现在我创建了一个标签作为链接到购物车页面的按钮。所以我让它工作,但是当我点击购物车按钮时,两个页面都会
根据 Web 标准,创建带有标题 1 的链接的正确代码是什么? 是吗 stackoverflow 或 stackoverflow 谢谢 最佳答案 根据网络标准,您不能将 block 元素放入内
在Java中它是这样写的..当我移植这段代码时...意识到没有这样的东西 break 和continue . 我知道这些命令没有包含在内,因为在使用带有命令的 goto 时必须有一种更简洁的方法来执
我们有一个相当标准的发布过程,使用 Visual Source Safe 在发布之前标记构建。这允许我们在出现任何问题时从该标签中获取,并在需要更改时使用它进行分支。 我们有几个不同的项目,并且总是使
我必须创建一个搜索内容,其中包含搜索框、标题和段落描述。默认情况下,描述被禁用,当我输入一些与描述文本匹配的文本时,描述段落标签应该打开。一些匹配的演示是这样的: [ fiddle ][1] 但默认情
我一直在阅读有关 的文档标签,我似乎无法理解它与简单地使用 有何不同那是 display: none; 文档:template tag 例子 对比 例子
我需要一个脚本来复制当开关按钮打开时标记,当开关按钮关闭时删除标记。我需要一个简单的方法。这是开关按钮: 我试过这个: var change
JSF 是一个 MVC 框架,但我很困惑为什么我们已经有了这么多 HTML 标签还需要 JSF 标签。毫无疑问,JSF 简化了很多事情。我想进一步了解 JSF 中的模型 View 和 Controll
我在这个 website 上看到了那些 html 代码: Homepa
我添加了 photoswipe 插件,可以使用 搜索我的所有照片。标签,如果点击,照片就会变成全屏。我让它工作了,但现在我的导航栏(有 标签)在点击时会触发 photoswipe 插件。 在 ph
标签
我正在尝试截断显示自 的文本标签,但它不工作。我将样式应用于其他标签样式并且它确实有效(我看到的示例中没有一个使用 标签)。我想知道是否有人可以向我解释为什么会这样(我不是最擅长 HTML/CSS
HTML 是这样的: Menu 1 Menu 2 Sub menu 2
我可以更改 TextInputLayout 的位置 float 标签(底部 float 标签)吗?我需要为波纹管 float 标签设置正确的位置。 最佳答案 我解决了我的问题,这是我的 xml:
我的代码是 printMsg : function(data) { $("#message").html(data.bodyText); ... } 这里 data.body
我是 Scrapy 和 Xpath 的初学者,我正在寻找解析具有以下结构的网站 cat1 value1 value2
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求提供代码的问题必须表现出对所解决问题的最低限度理解。包括尝试过的解决方案、为什么它们不起作用,以及
我必须从 xml 中解析数据。这是我的 xml- 或者它的 url 是:http://mobileecommerce.site247365.com/admin/catdata.xml News f
如何创建应该允许多行数据的标记。不要说使用textarea标签。我知道,但我只想 标记因为标签具有 value 属性。所以当我从 xml 文件获取值时,我应该使用 jquery 语法动态获取.. 最佳
我有一个页面使用我定义的某些样式。 在同一页面上,我刚刚导入了一个使用自己样式的外部 jQuery 插件,例如,包括 。被我自己覆盖的标签样式。 如何确保我的样式表中的样式不会覆盖 jQuery 插件
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 8 年前。 Improve
我是一名优秀的程序员,十分优秀!