android - 接收 Intent 操作 MAIN 而不是 NDEF 消息

标签 android nfc ndef android-beam android-applicationrecord

我正在开发一个带有 NFC 的应用程序。我正在使用一个示例,其中发送和接收数据由同一 Activity 承载。现在我需要将发送转移到另一台,由于某种原因,接收不再起作用。

这是我的主要内容:

<uses-permission android:name="android.permission.NFC" />

<uses-feature
    android:name="android.hardware.nfc"
    android:required="true" />
    <activity
        android:name=".Screens.LogIn.LogInActivity"
        android:launchMode="singleTop">

        <intent-filter>
            <action android:name="android.nfc.action.NDEF_DISCOVERED" />
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:mimeType="text/plain" />
        </intent-filter>

        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

以及接收 Activity :

public class LogInActivity extends AppCompatActivity {

public static final String TAG = LogInActivity.class.getSimpleName();

private String messagesToReceive= null;

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

    if (getIntent().getAction().equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) {
        handleNfcIntent(getIntent());
    }
}

private void replaceFragment() {
    android.support.v4.app.FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
    LogInFragment fragment = new LogInFragment();
    ft.replace(R.id.fragmentFrame, fragment, LogInFragment.TAG);
    ft.commit();
}

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

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


private void handleNfcIntent(Intent NfcIntent) {

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

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

            for (NdefRecord record:attachedRecords) {
                String string = new String(record.getPayload());
                if (string.equals(getPackageName())) { continue; }
                messagesToReceive = string;
            }
            Toast.makeText(this, "Received " +
                    " Messages", Toast.LENGTH_LONG).show();
        }
        else {
            Toast.makeText(this, "Received Blank Parcel", Toast.LENGTH_LONG).show();
        }
    }
}

问题是,NfcIntent.getAction() 始终等于 android.intent.action.MAIN,即使应用程序在我使用 NFC 发送数据时打开。如您所见, list 中有操作 NDEF_DISCOVERED

这是我发送的内容:

public NdefRecord[] createRecords() {
    NdefRecord[] records = new NdefRecord[2];
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        byte[] payload = messagesToSend.
                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[0] = record;
    }
    else {
        byte[] payload = messagesToSend.//messagesToSendArray.get(i).
                getBytes(Charset.forName("UTF-8"));

        NdefRecord record = NdefRecord.createMime("text/plain",payload);
        records[0] = record;
    }
    records[1] = NdefRecord.createApplicationRecord(getActivity().getPackageName());
    return records;
}


@Override
public NdefMessage createNdefMessage(NfcEvent event) {
    if (messagesToSend != null && !messagesToSend.equals("")) {
        return null;
    }
    NdefRecord[] recordsToAttach = createRecords();
    return new NdefMessage(recordsToAttach);
}

最佳答案

接收 NFC 事件的 Intent 操作 MAIN 而不是 NDEF_DISCOVERED 通常清楚地表明发送方发送了包含 Android 应用程序记录的 NDEF 消息,并且您未过滤该 NDEF 消息的第一条记录。

由于您的接收端有一个 NDEF_DISCOVERED Intent 过滤器,因此问题很可能位于发送端。查看您的代码,这可能有几个原因:

  1. 发送方在createNdefMessage()中创建NDEF消息。在那里,您可以测试 messagesToSend 是否为 null 或为空。如果它既不是 null 也不是空,则从 NDEF 消息创建回调返回 null:

    if (messagesToSend != null && !messagesToSend.equals("")) {
        return null;
    }
    

    由于您没有透露 messagesToSend 是否为 null 或为空,因此没有人可以知道您是否真正到达了创建自己的 NDEF 消息的阶段。但是,messagesToSend 很可能为 null/空,因为从 createNdefMessage() 返回 null 会完全禁用 Beam。

  2. 您没有透露发送方的剩余代码。因此,无法判断您是否确实注册了 createNdefMessage() 回调。你应该点个赞

    nfcAdapter.setNdefPushMessageCallback(this, this);
    

    onCreate()中。如果不是这种情况,Android 将使用默认的 NDEF 消息。默认 NDEF 消息通常包含应用程序的 URI 记录和 AAR。这可以解释为什么接收方会看到 Intent 操作 MAIN,因为您的 Activity 没有注册接收该特定 URI 记录。

  3. 在 Jelly Bean 以下的 Android 版本上,该情况

    (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
    

    计算结果为true,因此您将命中 if 语句的该分支。在此分支中,您将创建一个具有无效结构的 NFC 论坛文本记录。这可能会导致接收设备无法将 NDEF 记录解码为有效的文本记录。因此,接收设备可能无法将此记录与 text/plain 类型的 NDEF_DISCOVERED Intent 过滤器相匹配。

    为了解决此问题,您需要创建有效的文本记录。文本 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);
    }
    
  4. 当在线调用createRecords()方法时

    NdefRecord[] recordsToAttach = createRecords();
    

    messagesToSend 要么为 null,要么为空(因为否则您将返回 null)。如果 messagesToSend 特别是 null 并且您点击了该行

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

    createRecords() 中,这将导致 NullPointerException (因为您尝试访问 null 实例的方法)。由于您从未捕获此异常,因此回调 createNdefMessage() 将因相同的异常而失败。在这种情况下,Android 将处理此异常,并自动使用默认的 NDEF 消息(由应用程序的 URI 记录和 AAR 组成)。由于您的接收方不会过滤该 URI 记录(默认 NDEF 消息的第一个记录),因此您的接收方将仅由 AAR 标记,并且您将收到 Intent 操作 MAIN,因为 Android 假定您的 Activity 已准备好接收该记录具体的 NDEF 消息。

关于android - 接收 Intent 操作 MAIN 而不是 NDEF 消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45556316/

相关文章:

android - 让 HCE 应用程序优先于其他 NFC 标签,以便 HCE 成为唯一可读标签/首先被读取

java - 使用 NFC TAG 检测我的应用程序的问题

iOS11 核心 NFC 和 ISO 14443

Android AAR + Intent 过滤器 : clarification

android - Firebase 如何在 android 中处理 linkWithCredential "ERROR_CREDENTIAL_ALREADY_IN_USE"

java - 如何设置 java.util.logging.Formatter 使用的语言环境?

android - 如何检查用户是否在运行时在 Android 上授予了权限?

android - 可以使用传递参数通过 NFC 启动 android 应用程序

android - OnMapLoaded 在 Android 上永远不会被调用

适用于多种 NFC 类型的 Android Intent 过滤器