无法触发使用 LockTaskMode 读取 NFC 的意图

Unable to trigger intent on NFC read using with LockTaskMode

我正在尝试在我的 Android 应用程序中读取 NFC 标签,NFC 标签是带有纯文本的简单卡片,在查看 Android 文档和其他一些指南之后我'我的 AndroidManifest 中包含以下代码:

<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />

    <activity
        android:name=".pterm" // activity where i would be able to read NFC
        android:screenOrientation="portrait"
        android:theme="@style/SplashScreen">
        <intent-filter>
            <action android:name="android.nfc.action.NDEF_DISCOVERED" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:mimeType="text/plain" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.nfc.action.TAG_DISCOVERED" />
        </intent-filter>
    </activity>

并且在我的 activity 中添加了以下代码:

  @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        if (nfcRead) {
            readFromIntent(intent);
        }
    }

    private void readFromIntent(Intent intent) {
        String action = intent.getAction();
        if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)
                || NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)
                || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
            Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            NdefMessage[] messages = null;
            if (rawMessages != null) {
                messages = new NdefMessage[rawMessages.length];
                for (int i = 0; i < rawMessages.length; i++) {
                    messages[i] = (NdefMessage) rawMessages[i];
                    NdefRecord[] records = messages[i].getRecords();
                    //if you are sure you have text then you don't need to test TNF
                    for(NdefRecord record: records){
                        processRecord(record);
                    }
                }
            }
        }
    }

    public void processRecord(NdefRecord record) {

        short tnf = record.getTnf();
        switch (tnf) {

            case NdefRecord.TNF_WELL_KNOWN: {
                if (Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) {
                    String yourtext = processRtdTextRecord(record.getPayload());
                    Log.e("NFC:", yourtext);
                } else if (Arrays.equals(record.getType(), NdefRecord.RTD_URI)) {
                    return;
                } else if (Arrays.equals(record.getType(), NdefRecord.RTD_SMART_POSTER)) {
                    return;
                } else {
                    return;
                }
            }
            case NdefRecord.TNF_MIME_MEDIA: {
                if (record.toMimeType().equals("MIME/Type")) {
                    // handle this as you want
                } else {
                    //Record is not our MIME
                }
            }
            // you can write more cases
            default: {
                //unsupported NDEF Record
            }
        }
    }

    private String processRtdTextRecord(byte[] payload) {
        String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
        int languageCodeLength = payload[0] & 0063;

        String text = "";
        try {
            text = new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            Log.e("UnsupportedEncoding", e.toString());
        }
        return text;
    }

但是当我尝试读取 NFC 标签时,甚至没有触发 onNewIntent 事件,但在设备发出 NFC 通知声音时读取了 NFC。

该应用程序的目的是仅在自定义 AletDialog 启动时读取 NFC,一旦 NFC 读取了值,应将其放置在 EditText 中,并且只有在对话框启动时才能再次读取新值又起来了。

应用程序正在使用 LockTaskMode。

更新:

根据应用程序在 LockTaskMode 中的评论,这可能会改变 re-launch 应用程序向其提供 intent 的系统能力(我没有 LockTaskMode)

我建议您不要使用旧的 NFC API,当您处于限制性 LockTaskMode 时,必须使用启动或 re-launching 应用程序。

我建议您尝试更新更好的 enableReaderMode NFC API 来读卡。 https://developer.android.com/reference/android/nfc/NfcAdapter#enableReaderMode(android.app.Activity,%20android.nfc.NfcAdapter.ReaderCallback,%20int,%20android.os.Bundle)

enableReaderMode NFC API 会在您的应用程序中引发回调,启动一个新线程来处理传入的 NFC 数据,没有启动或 re-launch 您的应用程序和因此 LockTaskMode.

可能没有问题

enableReaderMode API 使用示例。


public class MainActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback{

private NfcAdapter mNfcAdapter;

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

    private void enableNfc(){
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if(mNfcAdapter!= null && mNfcAdapter.isEnabled()) {
            // READER_PRESENCE_CHECK_DELAY is a work around for a Bug in some NFC implementations.
            Bundle options = new Bundle();
            options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250);

            // Ask for all type of cards to be sent to the App as they all might contain NDEF data
            mNfcAdapter.enableReaderMode(this,
                    this,
                    NfcAdapter.FLAG_READER_NFC_A |
                            NfcAdapter.FLAG_READER_NFC_B |
                            NfcAdapter.FLAG_READER_NFC_F |
                            NfcAdapter.FLAG_READER_NFC_V |
                            NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
                    options);
        } else {
            NfcMessage();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if(mNfcAdapter!= null)
            mNfcAdapter.disableReaderMode(this);
    }


// This gets run in a new thread on Tag detection
// Thus cannot directly interact with the UI Thread
public void onTagDiscovered(Tag tag) {

// Get the Tag as an NDEF tag Technology
Ndef mNdef = Ndef.get(tag);

        // only process if there is NDEF data on the card
        if (mNdef != null) {
             NdefMessage mNdefMessage = mNdef.getCachedNdefMessage();

             // Now process the Ndef message
             ....

             // If success of reading the correct Ndef message
             // Make a notification sound (as we disabled PLATFORM_SOUNDS when enabling reader Mode)
             // As getting a confirmation sound too early causes bad user behaviour especial when trying to write to cards
        }

        // Ignore other Tag types.

}


....

原创 我将在此处保留原始答案,因为它在此处显示了系统 NFC 应用程序的工作,其中 enableForegroundDispatch 通过 re-launching 并暂停将其中包含 NFC 数据的意图直接传递给您的应用程序并恢复它触发 onNewIntent
它还解释了如果你没有 enableForegroundDispatch 那么系统 NFC 应用程序将 NFC 意图传递给 Android OS 这导致它找到一个可以处理这种类型的意图的应用程序,它通过查看应用程序的所有清单文件以匹配 Intent 过滤器。

因此,如果系统扫描到匹配的标签,Manifest Intent 过滤器将导致 Android 启动您的应用程序。

onNewIntent 用于

the activity is re-launched while at the top of the activity stack instead of a new instance of the activity being started, onNewIntent() will be called on the existing instance with the Intent that was used to re-launch it.

来自 https://developer.android.com/reference/android/app/Activity#onNewIntent(android.content.Intent)

因此,在您的应用不是 运行 的情况下,它无法被 re-launch 编辑,因此必须在 onCreate 中捕获 Intent。

所以在 onCreate 中输入类似

的内容
        Intent intent = getIntent();

        // Check to see if the App was started because of the Intent filter in the manifest, if yes read the data from the Intent
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
          readFromIntent(intent);
        }

现在如果你想在应用程序 运行 时覆盖接收 NFC 数据,你需要使用 enableForegroundDispatchonNewIntent

https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc#foreground-dispatch

可以在 onResume 中创建 Intent Filter 并启用前台调度(当您的应用不在前台,即在 onPause 中时,应禁用前台调度,如图所示)。

例如像


private NfcAdapter adapter;

public void onResume() {
    super.onResume();
    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 {
        // Only look for plain text mime type
        ndef.addDataType("text/plain");
    }
    catch (MalformedMimeTypeException e) {
        throw new RuntimeException("fail", e);
    }
    intentFiltersArray = new IntentFilter[] {ndef, };


    adapter = NfcAdapter.getDefaultAdapter(this);
    adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, null);
}

@Override
public void onPause() {
    super.onPause();
    adapter.disableForegroundDispatch(this);

}