前台 Activity 中的 NfcAdapter.enableReaderMode 是否应该覆盖意图标记调度系统?

Ada*_*hns 6 android nfc android-10.0

在 Android 10 中,我注意到我从操作系统收到一条 Toast 消息,指出“此 NFC 标签不支持应用程序”或“此 NFC 标签不支持应用程序”(取决于设备):

在此输入图像描述

奇怪的是,我看到 ToastenableReaderMode在前台 Activity 中处于活动状态。在所有以前的 Android 版本中,enableReaderMode都会覆盖 Android 意图标记调度系统。这是 Android 10 中的错误吗?

我知道enableForegroundDispatch也存在,并且该 API似乎确实覆盖了意图标签调度系统,即使在 Android 10 中也是如此。但我想继续控制仅由 提供的 NFC 发现声音enableReaderMode

我还知道我可以在清单中声明一个意图过滤器,以在继续使用的同时摆脱 Toast enableReaderMode,但这也会产生意想不到的副作用(例如,我的应用程序可能会在从设备主屏幕读取 NFC 标签时启动,我不想)。

vah*_*apt 2

是的,这似乎是一个错误,因为 Android 操作系统在以下条件下会触发新的 tagDiscovery 事件:

  • 使用enableReaderMode代替enableForegroundDispatch
  • 读卡或写卡
  • 当卡仍处于接近状态时,调用disableReaderMode。

由于这会触发操作系统级别的事件,因此它可以暂停重点活动,可能会显示 toast 屏幕或显示相关的应用程序选择框。

为了解决这个问题,

解决方法1:

  • 尝试循环连接卡,直到引发 IOException。(这意味着卡不再接近)
  • 然后调用disableReaderMode

缺点:您可能需要向用户显示一条消息,以将标签/卡从设备附近移除。

解决方法2:

  • 使用旧版的enableForegroundDispatch/disableForegroundDispatch以及readerMode

缺点:不会显示弹出窗口,但仍然会触发标签发现声音。

两种解决方案都不需要定义意图过滤器。

下面是实现这两种解决方法的示例代码。

import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.os.Bundle;
import android.widget.Toast;

import java.io.IOException;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity
{
    private NfcAdapter nfcAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        //Assuming nfc adapter is present and active, such checks are ignored for code clarity,
        //production code must check for hardware nfc adapter existence
        nfcAdapter = NfcAdapter.getDefaultAdapter(this); //Get the default NFC adapter from OS

        //Additional initialization code
    }

    private void onTagDiscovered(Tag tag)
    {
        try
        {
            if (tag == null)
                return;

            //Assumption: We're using an NFC card that supports IsoDep
            IsoDep iso = IsoDep.get(tag);

            if (iso == null)
                return;

            iso.setTimeout(1000);
            iso.connect();

            //doCardReadWrite(iso);

            iso.close();

            //Workaround 1
            //Wait until the card has been removed from the range of NFC
            //Then finish the activity
            while(true)
            {
                try
                {
                    iso.connect();
                    Thread.sleep(100);
                    iso.close();
                }
                catch (IOException | InterruptedException e)
                {
                    //On this example, once we're done with the activity, we want to close it
                    //then onPause event will call disableReaderMode, at that moment we don't want a card to be in proximity
                    onCardRemoved();
                    break;
                }
            }
            //End of Workaround 1
        }
        catch (IOException e)
        {
            Toast.makeText(this, "Tag disconnected. Reason: " + e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }

    private void onCardRemoved()
    {
        this.finish();
    }

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

        //Workaround 2
        //Legacy nfc reader activation method, just enabled it but we won't use it
        //(to fully support [e.g. OS version < v4.4.4] you must override onNewIntent(Intent intent) method as well)

        //create intent/tag filters to be used with enableForegroundDispatch
        PendingIntent pendingIntent = PendingIntent.getActivity(
                this,
                0,
                new Intent(this, this.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
                0
        );

        IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
        tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
        IntentFilter[] writeTagFilters = new IntentFilter[]{tagDetected};

        nfcAdapter.enableForegroundDispatch(this, pendingIntent, writeTagFilters, null);
        //End of Workaround 2

        //ReaderMode activation
        Bundle options = new Bundle();
        options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 1000);//Presence check interval
        final int READER_FLAGS = NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK | NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS;
        nfcAdapter.enableReaderMode(this, new NfcAdapter.ReaderCallback()
        {
            @Override
            public void onTagDiscovered(Tag tag)
            {
                MainActivity.this.onTagDiscovered(tag);
            }
        }, READER_FLAGS, options);
    }

    @Override
    protected void onPause()
    {
        nfcAdapter.disableReaderMode(this);

        //Workaround 2
        nfcAdapter.disableForegroundDispatch(this);

        super.onPause();
    }
}
Run Code Online (Sandbox Code Playgroud)