我的应用使用前台调度系统允许用户轻触他们的 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 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
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/
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为
查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html
我试图使用yard记录一些Ruby代码,尽管我所做的正是所描述的here或here#@param[Integer]thenumberoftrials(>=0)#@param[Float]successprobabilityineachtrialdefinitialize(n,p)#initialize...end虽然我仍然得到这个奇怪的错误@paramtaghasunknownparametername:the@paramtaghasunknownparametername:success然后生成的html看起来很奇怪。我称yard为:$yarddoc-mmarkdown我做错了什么?
我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的
有时我需要处理键/值数据。我不喜欢使用数组,因为它们在大小上没有限制(很容易不小心添加超过2个项目,而且您最终需要稍后验证大小)。此外,0和1的索引变成了魔数(MagicNumber),并且在传达含义方面做得很差(“当我说0时,我的意思是head...”)。散列也不合适,因为可能会不小心添加额外的条目。我写了下面的类来解决这个问题:classPairattr_accessor:head,:taildefinitialize(h,t)@head,@tail=h,tendend它工作得很好并且解决了问题,但我很想知道:Ruby标准库是否已经带有这样一个类? 最佳
我早就知道Ruby中的“常量”(即大写的变量名)不是真正常量。与其他编程语言一样,对对象的引用是唯一存储在变量/常量中的东西。(侧边栏:Ruby确实具有“卡住”引用对象不被修改的功能,据我所知,许多其他语言都没有提供这种功能。)所以这是我的问题:当您将一个值重新分配给常量时,您会收到如下警告:>>FOO='bar'=>"bar">>FOO='baz'(irb):2:warning:alreadyinitializedconstantFOO=>"baz"有没有办法强制Ruby抛出异常而不是打印警告?很难弄清楚为什么有时会发生重新分配。 最佳答案
在Ruby中是否有Gem或安全删除文件的方法?我想避免系统上可能不存在的外部程序。“安全删除”指的是覆盖文件内容。 最佳答案 如果您使用的是*nix,一个很好的方法是使用exec/open3/open4调用shred:`shred-fxuz#{filename}`http://www.gnu.org/s/coreutils/manual/html_node/shred-invocation.html检查这个类似的帖子:Writingafileshredderinpythonorruby?
我有一个div,它根据表单是否正确提交而改变。我想知道是否可以检查类的特定元素?开始元素看起来像这样。如果输入不正确,添加错误类。 最佳答案 试试这个:browser.div(:id=>"myerrortest").class_name更多信息:http://watir.github.com/watir-webdriver/doc/Watir/HTMLElement.html#class_name-instance_method另一种选择是只查看具有您期望的类的div是否存在browser.div((:id=>"myerrortes
我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_