在NFC接收时保持当前活动

时间:2017-08-09 18:33:11

标签: android android-intent nfc ndef android-beam

我有一个似乎工作正常的应用程序,可以很好地通过NFC传输数据。我有一个主要活动,一个传输数据的活动,以及一个接收数据的不同活动。

发件人活动效果很好,但当接收者获得NFC意图时,它会将应用重新启动回主要活动。

我不确定为什么会这样。我希望它拒绝任何推动,除非用户已经参与该活动,如果他们是,则继续参与该活动并处理NFC意图。

这是清单:

<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());
    }
}

1 个答案:

答案 0 :(得分:0)

您在发件人活动代码中已经解决了一些问题:

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

  2. 您写道,您希望在一个活动中发送NDEF消息,但您希望在另一个活动中接收NFC事件。但是,您已注册发件人活动以在清单中接收NDEF_DISCOVERED事件。如果您不想接收和处理这些事件,则无需在清单中使用NDEF_DISCOVERED意图过滤器。

  3. 此外,您无需在发件人活动中处理NDEF_DISCOVERED意图(即您可以安全地删除方法onNewIntent()handleNfcIntent())。

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

    +----------+---------------+--------------------------------------+
    | Status   | Language Code | Text                                 |
    | (1 byte) | (n bytes)     | (m bytes)                            |
    +----------+---------------+--------------------------------------+
    
    如果Status是UTF-8编码且n是IANA语言代码,则Language Code等于Text的{​​{1}}长度(例如&#34} ; en&#34;英语)。因此,编码该记录的正确方法是:

    Language Code
  5. 我不清楚为什么你在Jelly Bean以下的Android版本上创建NFC论坛文本记录,同时在Jelly Bean及更高版本上创建MIME类型记录。您应该保持一致并在所有平台上创建相同的记录类型(请参阅Method NdefRecord.createTextRecord("en" , "string") not working below API level 21):

    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);
    }
    
  6. 最后,在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); } 中,您将数组createRecords()创建为

    records

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

    NdefRecord[] records = new NdefRecord[1];
    

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

    records

    此外,您需要删除该行

    records[0] = record;
    

    因为这将使用Android应用程序记录覆盖索引0(records[messagesToSendArray.size()] = NdefRecord.createApplicationRecord(getPackageName()); 为0)的先前存储的NDEF记录。同样,这会导致您的主要活动开始,因为您没有在清单中注册该特定记录类型。

  7. 最后,如果您想要拒绝推送,除非用户在接收器活动中,您应该考虑使用前台调度系统。在这种情况下,您将完成从清单中删除所有NDEF_DISCOVERED意图过滤器,而是使用前台调度系统注册每个活动(接收方,发送方, main)。在接收器中,您将通过messagesToSendArray.size()接收NDEF消息。在发件人和主要活动中,您只需忽略并删除任何收到的NDEF消息。有关示例,请参阅Android app enable NFC only for one Activity