android - 接收到 NFC 后保持当前 Activity

标签 android android-intent nfc ndef android-beam

我有一个应用程序似乎工作正常并且可以很好地通过 NFC 传输数据。我有一个主要 Activity 、一个传输数据的 Activity 和一个接收数据的不同 Activity 。

发送方 Activity 运行良好,但当接收方收到 NFC Intent 时,它会将应用重新启动回到主 Activity。

我不太确定这是为什么。我希望它拒绝任何推送,除非用户已经在该 Activity 中,如果用户已经在该 Activity 中,则留在该 Activity 中并处理 NFC Intent 。

这是 list :

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
<activity android:name=".Timer" />
<activity android:name=".AddSlaves"
          android:label="Add Slave Devices"
          android:launchMode="singleTask">
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
</activity>
<activity android:name=".JoinSrv"
          android:launchMode="singleTask">
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain" />
    </intent-filter>
</activity>

这是发件人类:

public class JoinSrv extends Activity implements NfcAdapter.OnNdefPushCompleteCallback, NfcAdapter.CreateNdefMessageCallback {
    //The array lists to hold our messages
    private ArrayList<String> messagesToSendArray = new ArrayList<>();
    private ArrayList<String> messagesReceivedArray = new ArrayList<>();

    //Text boxes to add and display our messages
    private NfcAdapter mNfcAdapter;

    //Save our Array Lists of Messages for if the user navigates away
    @Override
    public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        savedInstanceState.putStringArrayList("messagesToSend", messagesToSendArray);
        savedInstanceState.putStringArrayList("lastMessagesReceived", messagesReceivedArray);
    }

    //Load our Array Lists of Messages for when the user navigates back
    @Override
    public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        messagesToSendArray = savedInstanceState.getStringArrayList("messagesToSend");
        messagesReceivedArray = savedInstanceState.getStringArrayList("lastMessagesReceived");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_join_srv);


        //Check if NFC is available on device
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (mNfcAdapter != null) {
            //Handle some NFC initialization here
        } else {
            Toast.makeText(this, "NFC not available on this device",
                    Toast.LENGTH_SHORT).show();
        }

        //Check if NFC is available on device
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if (mNfcAdapter != null) {
            //This will refer back to createNdefMessage for what it will send
            mNfcAdapter.setNdefPushMessageCallback(this, this);

            //This will be called if the message is sent successfully
            mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
        }
    }

    @Override
    public NdefMessage createNdefMessage(NfcEvent event) {
        //This will be called when another NFC capable device is detected.
        //We'll write the createRecords() method in just a moment
        NdefRecord[] recordsToAttach = createRecords();
        //When creating an NdefMessage we need to provide an NdefRecord[]
        return new NdefMessage(recordsToAttach);
    }

    @Override
    public void onNdefPushComplete(NfcEvent event) {
        //This is called when the system detects that our NdefMessage was
        //Successfully sent.
        messagesToSendArray.clear();
    }

    public NdefRecord[] createRecords() {
        NdefRecord[] records = new NdefRecord[1];
        //To Create Messages Manually if API is less than
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {

            byte[] payload = "192.168.1.100".
                    getBytes(Charset.forName("UTF-8"));
            NdefRecord record = new NdefRecord(
                    NdefRecord.TNF_WELL_KNOWN,      //Our 3-bit Type name format
                    NdefRecord.RTD_TEXT,            //Description of our payload
                    new byte[0],                    //The optional id for our Record
                    payload);                       //Our payload for the Record

            records[1] = record;

        }
        //Api is high enough that we can use createMime, which is preferred.
        else {

                byte[] payload = "192.168.1.100".
                        getBytes(Charset.forName("UTF-8"));

                NdefRecord record = NdefRecord.createMime("text/plain",payload);
                records[1] = record;

        }
        records[messagesToSendArray.size()] =
                NdefRecord.createApplicationRecord(getPackageName());
        return records;
    }

    private void handleNfcIntent(Intent NfcIntent) {
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(NfcIntent.getAction())) {
            Parcelable[] receivedArray =
                    NfcIntent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);

            if (receivedArray != null) {
                messagesReceivedArray.clear();
                NdefMessage receivedMessage = (NdefMessage) receivedArray[0];
                NdefRecord[] attachedRecords = receivedMessage.getRecords();

                for (NdefRecord record : attachedRecords) {
                    String string = new String(record.getPayload());
                    //Make sure we don't pass along our AAR (Android Application Record)
                    if (string.equals(getPackageName())) {
                        continue;
                    }
                    messagesReceivedArray.add(string);
                }
                Toast.makeText(this, "Received " + messagesReceivedArray.size() +
                        " Messages", Toast.LENGTH_LONG).show();
            } else {
                Toast.makeText(this, "Received Blank Parcel", Toast.LENGTH_LONG).show();
            }
        }
    }

    @Override
    public void onNewIntent(Intent intent) {
        handleNfcIntent(intent);
    }

    @Override
    public void onResume() {
        super.onResume();
        handleNfcIntent(getIntent());
    }
}

这是接收类:

public class AddSlaves extends Activity implements NfcAdapter.OnNdefPushCompleteCallback, NfcAdapter.CreateNdefMessageCallback{
    //The array lists to hold our messages
    private ArrayList<String> messagesToSendArray = new ArrayList<>();
    private ArrayList<String> messagesReceivedArray = new ArrayList<>();

    //Text boxes to add and display our messages
    private EditText txtBoxAddMessage;
    private TextView txtReceivedMessages;
    private TextView txtMessagesToSend;
    private NfcAdapter mNfcAdapter;

    private  void updateTextViews() {
        txtReceivedMessages.setText("Messages Received:\n");
        //Populate our list of messages we have received
        if (messagesReceivedArray.size() > 0) {
            for (int i = 0; i < messagesReceivedArray.size(); i++) {
                txtReceivedMessages.append(messagesReceivedArray.get(i));
                txtReceivedMessages.append("\n");
            }
        }
    }

    //Save our Array Lists of Messages for if the user navigates away
    @Override
    public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        savedInstanceState.putStringArrayList("lastMessagesReceived",messagesReceivedArray);
    }

    //Load our Array Lists of Messages for when the user navigates back
    @Override
    public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        messagesReceivedArray = savedInstanceState.getStringArrayList("lastMessagesReceived");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_slaves);

        txtReceivedMessages = (TextView) findViewById(R.id.txtMessagesReceived);
        Button btnAddMessage = (Button) findViewById(R.id.buttonAddMessage);


        //Check if NFC is available on device
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if(mNfcAdapter != null) {
            //Handle some NFC initialization here
        }
        else {
            Toast.makeText(this, "NFC not available on this device",
                    Toast.LENGTH_SHORT).show();
        }

        //Check if NFC is available on device
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
        if(mNfcAdapter != null) {
            //This will refer back to createNdefMessage for what it will send
            mNfcAdapter.setNdefPushMessageCallback(this, this);

            //This will be called if the message is sent successfully
            mNfcAdapter.setOnNdefPushCompleteCallback(this, this);
        }
    }

    @Override
    public NdefMessage createNdefMessage(NfcEvent event) {
        //This will be called when another NFC capable device is detected.
        return null;

    }

    @Override
    public void onNdefPushComplete(NfcEvent event) {
        //This is called when the system detects that our NdefMessage was
        //Successfully sent.
        messagesToSendArray.clear();
    }


    private void handleNfcIntent(Intent NfcIntent) {
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(NfcIntent.getAction())) {
            Parcelable[] receivedArray =
                    NfcIntent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);

            if(receivedArray != null) {
                messagesReceivedArray.clear();
                NdefMessage receivedMessage = (NdefMessage) receivedArray[0];
                NdefRecord[] attachedRecords = receivedMessage.getRecords();

                for (NdefRecord record:attachedRecords) {
                    String string = new String(record.getPayload());
                    //Make sure we don't pass along our AAR (Android Application Record)
                    if (string.equals(getPackageName())) { continue; }
                    messagesReceivedArray.add(string);
                }
                Toast.makeText(this, "Received " + messagesReceivedArray.size() +
                        " Messages", Toast.LENGTH_LONG).show();
                updateTextViews();
            }
            else {
                Toast.makeText(this, "Received Blank Parcel", Toast.LENGTH_LONG).show();
            }
        }
    }


    @Override
    public void onNewIntent(Intent intent) {
        handleNfcIntent(intent);
    }

    @Override
    public void onResume() {
        super.onResume();
        updateTextViews();
        handleNfcIntent(getIntent());
    }
}

最佳答案

您的发件人 Activity 代码中存在一些问题:

  1. 您存储了 messagesToSendArray,但实际上您从未用数据填充此数组列表(即 messagesToSendArray.size() 始终为 0)。由于每当调用 createNdefMessage() 时您都会重新创建 NDEF 消息,因此无需保存和恢复 messagesToSendArray

  2. 您写道,您希望在一个 Activity 中发送 NDEF 消息,但您希望在另一个 Activity 中接收 NFC 事件。但是,您注册了您的发送者 Activity 以在 list 中接收 NDEF_DISCOVERED 事件。如果您不想接收和处理这些事件,则无需在 list 中使用 NDEF_DISCOVERED Intent 过滤器。

  3. 此外,无需在您的发送者 Activity 中处理 NDEF_DISCOVERED Intent (即您可以安全地删除方法 onNewIntent()handleNfcIntent() ).

  4. 在 Jelly Bean 以下的 Android 版本中,您创建了一个结构无效的 NFC 论坛文本记录。文本 RTD 需要一个以表单编码的有效负载(另请参阅 this post )

    +----------+---------------+--------------------------------------+
    | Status   | Language Code | Text                                 |
    | (1 byte) | (n bytes)     | (m bytes)                            |
    +----------+---------------+--------------------------------------+
    
    where Status equals to the length n of the Language Code if the Text is UTF-8 encoded and Language Code is an IANA language code (e.g. "en" for English). Consequently, the proper way to encode that record would be:

    public static NdefRecord createTextRecord(String language, String text) {
        byte[] languageBytes;
        byte[] textBytes;
        try {
            languageBytes = language.getBytes("US-ASCII");
            textBytes = text.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new AssertionError(e);
        }
    
        byte[] recordPayload = new byte[1 + (languageBytes.length & 0x03F) + textBytes.length];
    
        recordPayload[0] = (byte)(languageBytes.length & 0x03F);
        System.arraycopy(languageBytes, 0, recordPayload, 1, languageBytes.length & 0x03F);
        System.arraycopy(textBytes, 0, recordPayload, 1 + (languageBytes.length & 0x03F), textBytes.length);
    
        return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, null, recordPayload);
    }
    
  5. 我不清楚为什么在 Jelly Bean 及以上版本的 Android 版本上创建 NFC 论坛文本记录,而在 Jelly Bean 及更高版本上创建 MIME 类型记录。您应该保持一致并在所有平台上创建相同的记录类型(请参阅 Method NdefRecord.createTextRecord("en" , "string") not working below API level 21 ):

    String text = "192.168.1.100";
    String language = "en";
    
    NdefRecord record;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        record = NdefRecord.createTextRecord(language, text);
    } else {
        record = createTextRecord(language, text);
    }
    
  6. 最后,在 createRecords() 中创建数组 records as

    NdefRecord[] records = new NdefRecord[1];
    

    因此,该数组在索引 0 处有一个可访问元素。但是,您稍后尝试访问元素 1:

    records[1] = record;
    

    这会导致 IndexOutOfBounds 异常。由于 createRecords() 由 Android 通过 createNdefMessage() 回调调用,回调失败(由于运行时异常)并且 Android 不会使用您的 NDEF 消息。相反,Android 将为您的应用使用默认的 NDEF 消息。此默认 NDEF 消息包含一个 Android 应用程序记录,它将导致您的主要 Activity 被调用(因为您的其他任何 Activity 都没有注册以针对默认 NDEF 消息的特定内容启动);见NFC tag detection is not calling onNewIntent and it's Launching From Main Activity .因此,您需要将存储新创建的 NDEF 记录的 records 中的偏移量更改为 0:

    records[0] = record;
    

    此外,您需要删除该行

    records[messagesToSendArray.size()] =
        NdefRecord.createApplicationRecord(getPackageName());
    

    因为这会用 Android 应用程序记录覆盖先前存储在索引 0 处的 NDEF 记录(messagesToSendArray.size() 为 0)。同样,这将导致您的主要 Activity 开始,因为您没有在 list 中注册该特定记录类型。

最后,如果你想拒绝推送,除非用户在接收 Activity 中,你应该考虑使用前台调度系统。在这种情况下,您将从 list 中完全删除所有 NDEF_DISCOVERED Intent 过滤器,而是将每个 Activity (接收者、发送者、 main)注册到前台调度系统。在接收器中,您将通过 onNewIntent() 接收 NDEF 消息。在发件人和主要 Activity 中,您只需忽略并丢弃任何收到的 NDEF 消息。参见 Android app enable NFC only for one Activity举个例子。

关于android - 接收到 NFC 后保持当前 Activity ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45598237/

相关文章:

android - 传递可打包的对象数组列表

transactions - EMV 在线非接触式卡支付中的 ARQC 和 ARPC。它是如何工作的?

android - NFC 提供什么样的带宽?

android - 哪些带 NFC 的手机完全支持 Mifare Classic(1K 和 4K)卡?

android - Android手机转动时的OpenGL

android - 在 Android 中使用 PdfJet 库在 Box 上绘制图像时遇到问题

android - 如何更好地控制 Android SAF UI(例如 ACTION_OPEN_DOCUMENT)?

android - 调用一个新的 onCreateInputView()

android - 如何解决错误:任务应用程序preDexDebug的执行失败,退出值非零1

java - ByteArrayOutputStream 对 RAM 内存的极端使用