2017-08-09 106 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