为什么 getNdefMessage() returns 为 null,即使调试器显示 ndef 对象具有据称格式正确的消息负载?

Why does getNdefMessage() returns null even though debugger shows ndef object has a message payload that is supposedly correctly formatted?

我正在尝试使用 android phone(如果相关,则为 OnePlus 6T)连续轮询 NFC 标签。当我的标签仅由 NFC 提供支持时,即使调试器显示有效负载不为空并且格式正确(下面的屏幕截图),我也会得到空值。

getNdefMessage returns null

时的调试器输出

当返回值不为空时调试器输出

我用来轮询标签的代码,基于 and

protected void onResume() {
    super.onResume();

    if (nfcAdapter != null) {
        if (!nfcAdapter.isEnabled()) {
            Intent intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
            startActivity(intent);
        }

        Bundle options = new Bundle();

        options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250);

        nfcAdapter.enableReaderMode(this, new NfcAdapter.ReaderCallback() {
                    @Override
                    public void onTagDiscovered(Tag tag) {
                        Log.d("Tag", "New tag detected, attempting connect");
                        readMessage(tag);
                        tagInRange = true;
                    }
                },
                NfcAdapter.FLAG_READER_NFC_A
                        | NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS, options);

    }
}

读取消息函数,msg变量是一个private volatile NdefMessage:

private void readMessage(Tag tag) {
    Log.d("Tag", "Starting thread");
    new Thread(() -> {

        try {
            Thread.sleep(1800);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        final Ndef ndef = Ndef.get(tag);
        Log.d("Tag", "New tag detected, attempting connect");

        while (tagInRange) {

            try {
                ndef.connect();
                msg = ndef.getNdefMessage();

                Log.d("Tag", "Running on UI thread");
                if (msg == null) {
                    Log.d("Tag", String.valueOf(ndef));
                    continue;
                }
                runOnUiThread(() -> {
                    parseMessage(msg);
                    dataElapsedTime += 0.2;
                });

            } catch (IOException | FormatException e) {
                e.printStackTrace();
                tagInRange = false;
                System.out.println("Tag Connection Lost");
            }

            finally {

                try {
                    ndef.close();
                    Log.d("tag", "Tag connection closed successfully");
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }


        }

    }).start();

}

getNdefMessage 函数是否使用某种延迟加载?或者这是生成多个线程并发生一些奇怪的竞争条件的结果?

更新 2

查看 ndef object 的来源,您突出显示的 mNdefMsg 是系统在向您传递 Tag 对象之前读取的缓存 Ndef 消息

你似乎有一个自定义行为标签,可能这个标签不允许你在不通过 disconnect/connect 机制的情况下读取相同的数据两次。

根据您的调试数据,我会使用 ndef.getCachedNdefMessage() 来读取您突出显示的数据,但这可能无法满足您的需求。

一些背景,当 Android NFC 服务正常检测到标签时,它会尝试从中读取 Ndef 消息,因为它可能需要自己解析 Ndef 消息。

当您调用 ndef.getNdefMessage() 时,它实际上会直接从标签重新读取数据并忽略系统 NFC 服务之前读取的内容。

我会尝试启用 FLAG_READER_SKIP_NDEF_CHECK 以停止系统读取 Ndef 消息并用完你一次读取自定义标签的机会。

所以尝试添加那个标志,例如


nfcAdapter.enableReaderMode(this, new NfcAdapter.ReaderCallback() {
                    @Override
                    public void onTagDiscovered(Tag tag) {
                        Log.d("Tag", "New tag detected, attempting connect");
                        readMessage(tag);
                        tagInRange = true;
                    }
                },
                NfcAdapter.FLAG_READER_NFC_A
                        | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK
                        | NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS, options);

原创

我认为问题是你的 ndef.close() 放错了地方。

你不应该关闭标签,除非你不想再与之通信。

在 while 循环的第一次迭代中,它使用 ndef.connect(); 执行 try 块,并且 finally 始终在前面的 try 块中的代码之后执行。

真的,你应该只 ndef.close() 在 while 块之外。

调整后的代码(您还应该从 getNdefMessage 捕获 TagLostException

private void readMessage(Tag tag) {
    Log.d("Tag", "Starting thread");
    new Thread(() -> {

        try {
            Thread.sleep(1800);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        final Ndef ndef = Ndef.get(tag);
        Log.d("Tag", "New tag detected, attempting connect");
    
        try { 
          ndef.connect();
        } catch (IOException e) {
                e.printStackTrace();
                tagInRange = false;
                System.out.println("Failed to Connect");
        }

        while (tagInRange) {

            try {
                msg = ndef.getNdefMessage();

                Log.d("Tag", "Running on UI thread");
                if (msg == null) {
                    Log.d("Tag", String.valueOf(ndef));
                    continue;
                }
                runOnUiThread(() -> {
                    parseMessage(msg);
                    dataElapsedTime += 0.2;
                });

            } catch (TagLostException | IOException | FormatException e) {
                e.printStackTrace();
                tagInRange = false;
                System.out.println("Tag Connection Lost");
            }
        }
        
        // End of while loop try close just on the safe side
        
        try {
                    ndef.close();
                    Log.d("tag", "Tag connection closed successfully");
                } catch (IOException e) {
                    e.printStackTrace();
                }

    }).start();

}

请注意,您可能认为原始标签调试输出中仍然有 Ndef 消息,但由于您没有禁用 FLAG_READER_SKIP_NDEF_CHECK 那么这可能是 ndef.getCachedNdefMessage()[=28= 的缓存数据]

如果您查看 ndef.getCachedNdefMessage 的文档,您会看到它说它不会导致 RF activity 因为这只是 returns 系统 NFC 读取时的 NDEF 消息它首先检测到标签并在标签对象中传递给您。

其中 getNdefMessage 导致 I/O (RF) activity 并在调用时读取 current Ndef 消息(可能是与系统之前读取的不同)

更新

我好像记得你不能多次调用 connect() 并且文档说“一次只能将一个 TagTechnology 对象连接到一个标签。”即使它是相同的标签技术也是如此可能不是同一个实例,所以在示例中我已经将 connect 移出了 while 循环。

通过了解以下内容最终解决了此问题:

  1. ndef对象中的mNdefmessage变量对应缓存的消息,只要连接同一个标签实例就会保持不变。我读错了对象,并认为那是我想要的消息。感谢安德鲁指出这一点!

  2. 我收到的 null 不是调用 connect/close 的地方的结果,android 代码需要对最初发布的代码进行最小的更改。在函数 getNdefMessage() 的文档中说,如果标记处于初始化阶段,函数可能 return null。事实证明这是真正的问题,只有当我将我的标签 (NHS3152) 设置为在启动时闪烁 LED 并注意到它在从连接的电阻器获取一组读数后重新启动几次后才发现,这不应该是正在发生(这也导致了很多标记 lost/null 对象异常)。

  3. 由于这方面的文档很少,标签 restarting/reset 的原因是当由 NFC 供电时,它无法处理其输出引脚上超过特定阈值的电压场只和连接到一个负载。这导致了当连接到调试板 (LPCLink2) 或由 NFC 供电但未连接到负载时读取标签时一切正常的奇怪场景。最终通过更改 DAC 引脚上的输出电压解决了这个问题。