当前位置: 首页 > 知识库问答 >
问题:

Android NFC-Ndef.WritenDefMessage()抛出IOException并擦除标记数据

沈俊明
2023-03-14

我的应用程序使用前台调度系统,允许用户点击他们的NFC标签,以便在标签上执行读写操作。

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
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)

我的应用程序使用的NFC标签包含重要信息(即无意中删除标签数据不是一个选项!),所以我的问题是...

  1. 为什么我的代码(参见下面)会这样擦除标记数据?
  2. 如何修复根本问题,或者是否有可接受的解决方法?
  3. 是否值得我尝试使用NfcA或IsoDep而不是NDEF?

做了很多搜索,我很惊讶这个问题没有在其他地方讨论过,所以如果问题不是和我的代码有关,那么会不会是和我正在使用的NFC标签有关呢...

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();

            }
        });
    }

}

共有1个答案

司空鸿熙
2023-03-14

我真的很想知道,当您在覆盖存储设备的数据过程中移除存储设备时,您还会期望发生什么。

您的代码并不是真正地“擦除”数据。它只是从标记存储器的开始就开始覆盖数据,当您中断写入时,将标记留在未定义的状态。

NFC标签一次只支持存储一条NDEF消息。因此,当您开始编写新的NDEF消息时,需要覆盖旧的NDEF消息。因此,

ndef.writeNdefMessage(ndefMessageNew);

这可能是另一种选择:不对关键数据使用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/MF Ultralight上的一个块有4个字节)。

然后,您将使用类似于以下内容来读取和更新您的标记:

    null

>

  • 读取:

    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
    });
    

  •  类似资料:
    • 问题内容: 有人可以解释此代码吗? 看起来似乎很奇怪,但这不会产生强制转换异常,并允许抛出已检查的异常而不必在签名中声明它或将其包装在未检查的异常中。 注意,或者main都没有声明任何检查过的异常,但是输出是: 在Lombok中使用此hack,带有@SneakyThrow批注,该批注允许抛出已检查的异常而无需声明它们。 我知道它与类型擦除有关,但是我不确定要了解hack的每个部分。 编辑: 我知道

    • 问题内容: 在这里提供一些答案并阅读了一些评论之后,看来,实际上IOException永远不会对文件I / O抛出异常。 在任何情况下,在Stream / Reader / Writer上进行close调用实际上会引发IOException吗? 如果实际引发异常,应该如何处理? 问题答案: 对于文件,可能不会在close()上经常抛出IOException,但是对于非文件I / O,您肯定会看到它

    • 我有一个任务要将我的抛出异常从main()“移动”到lambda-expression。这意味着当在Lambda中发生异常时,程序将使用来自main的抛出。问题是,我不能创建任何其他的接口来自动实现这一功能,因为我的老师说只能使用java.util.function的接口,我也一直在网上找,但大多数答案都是“创建新的接口”。

    • 我有以下标签,它们工作得很好 它们位于form:form标签中。页面加载良好。 有什么想法吗?谢谢

    • 标记-清除是主流的追踪式收集算法的一种,和引用计数相比,追踪式收集是一种间接方法,与程序耦合性很低,简单地说,就是程序在需要的时候只管申请内存,使用时也不用像引用计数机制要做实时地维护,直到一定条件(一般是已申请内存空间达到一定阈值),进程暂停执行,由垃圾收集器回收垃圾,然后继续执行,当然如果收集之后还是内存不足,就报错。很多语言的主流实现都使用追踪式收集,比如java,C#等 追踪式收集严格按照

    • 我使用JSoup解析字符串,其中包含超文本标记语言标记为纯文本。例如: 它可以很好地解析它,但问题是我的Java字符串是否包含