2017-08-09 105 views
2

我有一個應用程序,似乎工作正常,可以通過NFC完美地傳輸數據。我有一項主要活動,一項傳輸數據的活動以及一項接收數據的不同活動。在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()); 
    } 
} 
+0

我猜你的問題是由於'singleTask'啓動模式。你爲什麼指定?請解釋您的應用程序導航。 –

+0

此外,您還有2項活動可以處理'NDEF_DISCOVERED'動作。這意味着Android在掃描NFC標籤時不知道要開始哪一個。你如何處理這個問題? –

回答

0

你有qute中的幾個問題你發件人活動代碼:

  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 requres即在形式編碼的有效載荷(也參見this post

     
    +----------+---------------+--------------------------------------+ 
    | Status | Language Code | Text         | 
    | (1 byte) | (n bytes)  | (m bytes)       | 
    +----------+---------------+--------------------------------------+ 
    
    其中Status等於該長度的Language Code如果Text是UTF-8編碼和Language Coden是IANA語言代碼(例如,「en」表示英語)。因此,對編碼記錄的正確方法應該是:

    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. 爲什麼你創建的Android版本低於果凍豆的NFC論壇文字實錄而創建的果凍豆及以上的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作爲

    NdefRecord[] records = new NdefRecord[1]; 
    

    因此,該數組有一個元素在索引0處可訪問。Hoever,您稍後嘗試訪問元素1:

    records[1] = record; 
    

    這會導致IndexOutOfBounds異常。由於createRecords()由Android通過createNdefMessage()回調調用,因此回調失敗(由於運行時異常),Android不會使用您的NDEF消息。相反,Android將爲您的應用使用默認的NDEF消息。這個默認的NDEF消息包含一個Android應用程序記錄,它將導致你的主要活動被調用(因爲你的其他活動沒有被註冊爲啓動默認NDEF消息的特定內容);見NFC tag detection is not calling onNewIntent and it's Launching From Main Activity。因此,你需要改變在您新創建NDEF記錄存儲爲0 records偏移:

    records[0] = record; 
    

    此外,你需要刪除線

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

    ,因爲這會再覆蓋上一存儲在索引爲0的NDEF記錄(messagesToSendArray.size()爲0)與Android應用程序記錄。同樣,這會導致您的主要活動開始,因爲您沒有在清單中註冊該特定記錄類型。

最後,如果你想拒絕推送,除非用戶在接收者活動中,你應該考慮使用前臺調度系統。在這種情況下,您需要從清單中刪除所有NDEF_DISCOVERED意向過濾器,並將每個活動(接收者,發件人, main)註冊到前臺調度系統。在接收器中,您將通過onNewIntent()收到NDEF消息。在發件人和主要活動中,您只需忽略並放棄任何收到的NDEF消息。一個例子見Android app enable NFC only for one Activity