如何在不覆盖现有值的情况下将两个活动的值写入 NFC 标签?

How to write values to NFC tag from two activities without overwriting existing values?

我正在尝试编写一个 Android 应用程序,该应用程序具有两个将多个值 (Strings/text) 写入同一个 NFC 标签的活动。我已经设法让一个 activity 将值写入标签,但是如何防止第二个 activity 覆盖第一个 activity 的值?

public class MainActivity extends AppCompatActivity {

NfcAdapter nfcAdapter;
ToggleButton tglReadWrite;
Button anotherActivtybtn;
EditText txtName;
EditText txtCountry;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    nfcAdapter = NfcAdapter.getDefaultAdapter(this);
    tglReadWrite = (ToggleButton)findViewById(R.id.tglReadWrite);
    txtName = (EditText)findViewById(R.id.idName);
    txtCountry= (EditText)findViewById(R.id.idCountry);
    anotherActivtybtn= (Button)findViewById(R.id.anotherActivtybtn);

    anotherActivtybtn.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent= new Intent(MainActivity.this, AnotherActivity.class);
            startActivity(intent);
        }
    });
}

@Override
protected void onResume() {
    super.onResume();
    if(nfcAdapter !=null){

        /* go to phone's nfc settings */
        if(!nfcAdapter.isEnabled()){
            showNfcSettings();
        }
        enableForegroundDispatchSystem();
    }

}

private void showNfcSettings() {
    Intent nfcSettingIntent = new Intent(Settings.ACTION_NFC_SETTINGS);
    startActivity(nfcSettingIntent);
}

@Override
protected void onPause() {
    super.onPause();

    disableForegroundDispatchSystem();
}

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);

    if (intent.hasExtra(NfcAdapter.EXTRA_TAG)) {
        Toast.makeText(this, "NfcIntent!", Toast.LENGTH_SHORT).show();

        if(tglReadWrite.isChecked())
        {
            Parcelable[] parcelables = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);

            if(parcelables != null && parcelables.length > 0)
            {
                readTextFromMessage((NdefMessage) parcelables[0]);
            }else{
                Toast.makeText(this, "No NDEF messages found!", Toast.LENGTH_SHORT).show();
            }

        }else{
            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            NdefMessage ndefMessage = createNdefMessage(txtName.getText().toString(), txtCountry.getText().toString());

            writeNdefMessage(tag, ndefMessage);
        }

    }
}

private void readTextFromMessage(NdefMessage ndefMessage) {
    NdefRecord[] ndefRecords = ndefMessage.getRecords();

    if(ndefRecords != null && ndefRecords.length>0){

        NdefRecord ndefRecord = ndefRecords[0];
        NdefRecord ndefRecord2 = ndefRecords[1];

        String tagContent = getTextFromNdefRecord(ndefRecord);
        String tagContent2 = getTextFromNdefRecord(ndefRecord2);

        txtName.setText(tagContent);
        txtCountry.setText(tagContent2);

    }else
    {
        Toast.makeText(this, "No NDEF records found!", Toast.LENGTH_SHORT).show();
    }

}

private void enableForegroundDispatchSystem() {
    Intent intent = new Intent(this, MainActivity.class).addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);

    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);

    IntentFilter[] intentFilters = new IntentFilter[]{};

    nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilters, null);
}

private void disableForegroundDispatchSystem() {
    nfcAdapter.disableForegroundDispatch(this);
}

private void formatTag(Tag tag, NdefMessage ndefMessage) {
    try {
        NdefFormatable ndefFormatable = NdefFormatable.get(tag);

        if (ndefFormatable == null) {
            Toast.makeText(this, "Tag is not ndef formatable!", Toast.LENGTH_SHORT).show();
        }

        else{
            ndefFormatable.connect();
            ndefFormatable.format(ndefMessage);
            ndefFormatable.close();

            Toast.makeText(this, "Tag writen!", Toast.LENGTH_SHORT).show();
        }
    } catch (Exception e) {
        Log.e("formatTag", e.getMessage());
    }
}

private void writeNdefMessage(Tag tag, NdefMessage ndefMessage) {
    try {

        if (tag == null) {
            Toast.makeText(this, "Tag object cannot be null", Toast.LENGTH_SHORT).show();
            return;
        }

        Ndef ndef = Ndef.get(tag);

        if (ndef == null) {
            // format tag with the ndef format and writes the message.
            formatTag(tag, ndefMessage);
        } else {
            ndef.connect();
            if (!ndef.isWritable()) {
                Toast.makeText(this, "Tag is not writable!", Toast.LENGTH_SHORT).show();

                ndef.close();
                return;
            }

            ndef.writeNdefMessage(ndefMessage);
            ndef.close();

            Toast.makeText(this, "Tag writen!", Toast.LENGTH_SHORT).show();

        }

    } catch (Exception e) {
        Log.e("writeNdefMessage", e.getMessage());
    }

}

private NdefRecord createTextRecord(String content) {
    try {
        byte[] language;
        language = Locale.getDefault().getLanguage().getBytes("UTF-8");

        final byte[] text = content.getBytes("UTF-8");
        final int languageSize = language.length;
        final int textLength = text.length;
        final ByteArrayOutputStream payload = new ByteArrayOutputStream(1 + languageSize + textLength);

        payload.write((byte) (languageSize & 0x1F));
        payload.write(language, 0, languageSize);
        payload.write(text, 0, textLength);

        return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], payload.toByteArray());

    } catch (UnsupportedEncodingException e) {
        Log.e("createTextRecord", e.getMessage());
    }
    return null;
}

private NdefMessage createNdefMessage(String content, String content2) {

    NdefRecord ndefRecord = createTextRecord(content);
    NdefRecord ndefRecord2 = createTextRecord(content2);

    NdefMessage ndefMessage = new NdefMessage(new NdefRecord[]{ndefRecord, ndefRecord2});

    return ndefMessage;
}

public void tglReadWriteOnClick(View view){
    txtName.setText("");
    txtCountry.setText("");
}

public String getTextFromNdefRecord(NdefRecord ndefRecord)
{
    String tagContent = null;
    try {
        byte[] payload = ndefRecord.getPayload();
        String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
        int languageSize = payload[0] & 0063;
        tagContent = new String(payload, languageSize + 1,
                payload.length - languageSize - 1, textEncoding);
    } catch (UnsupportedEncodingException e) {
        Log.e("getTextFromNdefRecord", e.getMessage(), e);
    }
    return tagContent;
}
}

有办法吗?

一个 NFC 论坛类型标签只能包含一个 NDEF 消息。因此,方法 ndef.writeNdefMessage(ndefMessage) 将始终使用作为参数传递的新消息覆盖任何现有的 NDEF 消息 ndefMessage.

当然,该 NDEF 消息可能包含任意数量的 NDEF 记录(或者实际上适合您的标签)。

如果您想保留现有数据,则需要先阅读现有的 NDEF 消息,例如通过

Parcelable[] parcelables = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (parcelables != null && parcelables.length > 0) {
    NdefMessage ndefMessage = (NdefMessage)parcelables[0];
    ...

然后您可以解析此消息和 keep/drop 您想要 preserve/remove 的部分(NDEF 记录或数据元素)。最后,您可以将新部分添加到该消息并将整个消息再次写入标签。