【问题标题】:Unable to trigger intent on NFC read using with LockTaskMode使用 LockTaskMode 无法触发 NFC 读取意图
【发布时间】:2020-10-20 08:34:30
【问题描述】:

我正在尝试在我的 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>

在我的活动中,我添加了以下代码:

  @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 中,并且只有在 Dialog 再次启动时才能再次读取新值.

应用程序正在使用 LockTaskMode。

【问题讨论】:

    标签: android nfc


    【解决方案1】:

    更新:

    基于应用程序位于LockTaskMode 的评论,这可能会改变系统重新启动应用程序以向其提供intent 的能力(我没有LockTaskMode 的经验)

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

    我建议您尝试更新更好的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 数据,您的应用程序不会启动或重新启动,因此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 通过重新启动它并暂停和恢复它来直接将带有 NFC 数据的意图传递给您的应用程序。 onNewIntent
    它还解释了如果您没有enableForegroundDispatch,那么系统 NFC 应用程序会将 NFC 意图传递给 Android 操作系统,这会导致它找到一个可以处理这种类型意图的应用程序,它通过查看所有应用程序的清单文件来完成要匹配的意图过滤器。

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

    onNewIntent 是为了

    活动在活动堆栈顶部重新启动,而不是正在启动的活动的新实例,onNewIntent() 将使用用于重新启动它的 Intent 在现有实例上调用。

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

    因此,在您的应用未运行的这种情况下,它无法重新启动,因此必须在 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 过滤器并启用前台调度(当您的应用不在前台即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);
    
    }
    
    

    【讨论】:

    • 我对在读取 NFC 后打开应用程序不感兴趣,而只是想在 AlertDialog 启动时从 nfc 卡读取数据并将其数据设置在 EditText 中,尝试后在 onCreate 中设置Intent,或者在调试中使用enableForegroundDispatchonNewIntent 我仍然无法读取readFromIntent 方法,并且应用程序仍在尝试重新启动(但由于它处于 LockTaskMode 中,所以它只是强制转换解锁它的消息)
    • 根据您使用的新信息 LockTaskMode 更新了答案,因为这可能会干扰使用旧的基于 Intent 的方法读取 NFC。
    • 我还应该说最好始终捕获 Activity 中的所有 NFC 类型,然后选择仅在当时显示警报对话框时处理它。这意味着当警报对话框未显示时,操作系统不会尝试执行任何操作(包括播放已发现标签的声音)(enableReaderMode 的另一个好处是控制已发现标签的声音)
    • 我实际上在我正在调用 dialog.show() 的方法中添加了enableNFC(),并且我正在调用 disableReaderMode() 关闭它,当一个新标签被读取并且它是模式.equals 到我正在寻找的东西,我正在做 Sond 通知并接受读取的数据,谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 2014-01-21
    • 1970-01-01
    • 2021-11-07
    • 2014-10-16
    • 1970-01-01
    相关资源
    最近更新 更多