仅从当前应用读取 NFC 标签

Read NFC Tag Exclusively From Current App

我试图从我的应用程序中读取 NFC 标签,但每次我扫描标签时它都会打开一个默认应用程序(我认为它是 Android 的默认应用程序...)

我想阻止任何标签在我的应用程序打开时在外部被读取。 我怎样才能做到这一点?

ScanNFC.java(Activity读取NFC数据):

public class ScanNFC extends AppCompatActivity {


public static final String MIME_TEXT_PLAIN = "text/plain";
public static final String TAG = "NfcDemo";

private NfcAdapter mNfcAdapter;

private TextView mTextView;


@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_scan_nfc);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    if (toolbar != null)
    {
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    }


    mTextView = (TextView) findViewById(R.id.tvTapNFC);
    mNfcAdapter = NfcAdapter.getDefaultAdapter(this);


    if (mNfcAdapter != null) {
        mTextView.setText("Read an NFC tag");
    } else {
        mTextView.setText("This phone is not NFC enabled.");
    }


    handleIntent(getIntent());
}


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

    setupForegroundDispatch(this, mNfcAdapter);
}

@Override
protected void onPause() {

    stopForegroundDispatch(this, mNfcAdapter);

    super.onPause();
}

@Override
protected void onNewIntent(Intent intent) {

    handleIntent(intent);
}


private void handleIntent(Intent intent)
{
    String action = intent.getAction();


    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {

        String type = intent.getType();
        if (MIME_TEXT_PLAIN.equals(type)) {

            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            new AsyncNdefReaderTask().execute(tag);

        } else {
            Log.d(TAG, "Wrong mime type: " + type);
        }
    } else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {

        Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        String[] techList = tag.getTechList();
        String searchedTech = Ndef.class.getName();

        for (String tech : techList) {
            if (searchedTech.equals(tech)) {
                new AsyncNdefReaderTask().execute(tag);
                break;
            }
        }
    }
}


private class AsyncNdefReaderTask extends NdefReaderTask
{
    @Override
    protected void onPostExecute(String result) {

        Log.d("onPostExecute", "onPostExecute");

        if (result != null) {
            mTextView.setText("Read content: " + result);
        }
    }
}


public static void setupForegroundDispatch(final Activity activity, NfcAdapter adapter)
{
    final Intent intent = new Intent(activity.getApplicationContext(), activity.getClass());
    intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

    final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0);

    IntentFilter[] filters = new IntentFilter[1];
    String[][] techList = new String[][]{};

    filters[0] = new IntentFilter();
    filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
    filters[0].addCategory(Intent.CATEGORY_DEFAULT);
    try {
        filters[0].addDataType(MIME_TEXT_PLAIN);
    } catch (IntentFilter.MalformedMimeTypeException e) {
        throw new RuntimeException("Check your mime type.");
    }

    adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList);
}


public static void stopForegroundDispatch(final Activity activity, NfcAdapter adapter) {
    adapter.disableForegroundDispatch(activity);
}

}

AndroidMAnifest.xml:

<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />


<application
    android:name=".MyApplication"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <activity
        android:name=".ScanNFC"
        android:label="@string/title_activity_scan_nfc"
        android:theme="@style/AppTheme.NoActionBar">

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

    </activity>
</application>

当我的应用程序处于 运行 时,如何防止其他应用程序读取 NFC 标签?

关键是使用前台调度系统 (NfcAdapter.enableForegroundDispatch()) 或 reader-模式 API (NfcAdapter.enableReaderMode())。请注意,后者仅适用于 Android 4.4+.

您已经尝试在您的代码中使用前台调度系统。但是,您目前只注册了一个非常具体的标签类型(可能与您的标签不匹配):

public static void setupForegroundDispatch(final Activity activity, NfcAdapter adapter) {
    final Intent intent = new Intent(activity.getApplicationContext(), activity.getClass());
    intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

    final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0);

    IntentFilter[] filters = new IntentFilter[1];
    String[][] techList = new String[][]{};

    filters[0] = new IntentFilter();
    filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
    filters[0].addCategory(Intent.CATEGORY_DEFAULT);
    try {
        filters[0].addDataType(MIME_TEXT_PLAIN);
    } catch (IntentFilter.MalformedMimeTypeException e) {
        throw new RuntimeException("Check your mime type.");
    }

    adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList);
}

使用该代码,您可以注册为仅接收包含 NDEF 文本记录(或 text/plain MIME 类型记录)的标签的事件。

您可以通过简单地使用:

来注册接收关于 any 标签的通知
public static void setupForegroundDispatch(final Activity activity, NfcAdapter adapter) {
    final Intent intent = new Intent(activity, activity.getClass());
    intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

    final PendingIntent pendingIntent = PendingIntent.getActivity(activity, 0, intent, 0);

    adapter.enableForegroundDispatch(activity, pendingIntent, null, null);
}