アクティビティが起動中のときだけ NFC に反応させる(Foreground Dispatch)

最近、 NFC タグで遊んでます。
でもなかなか、アプリのネタが思いつかないものです。
さて、今回は Android アプリで NFC アプリを開発する際のポイントのひとつについて書きます。

今だけ反応して欲しいんだけど…

たとえば、 NFC タグにデータ書き込みを行うアクティビティを作成していたとします。
NFC タグに反応するように、通常は以下のようにインテントフィルターをマニフェストファイルに書くと思います。

<activity
    android:name="NFC にデータを書き込むアクティビティ"
    android:label="@string/app_name">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <action android:name="android.nfc.action.TAG_DISCOVERED" />

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

しかし、上記の形式では

  • アプリを起動していなくても、 NFC タグをかざすと常に書き込もうとする

ことになります。
でも、 NFC を使う場合って、書き込むよりは読み込む場面の方が多いですよね。
なので、

  • NFC のデータを読み込むアプリ(通常はこっちで反応して欲しい)
  • NFC へデータを書き込むアプリ(書き込み用アクティビティが表示されてるときだけ反応して欲しい)

ということが求められます。

Foreground Dispatch

この要求を満たすために用意されている仕組みが、 Foreground Dispatch です。

マニフェストファイルにインテントフィルターを指定してしまうと、上記のように常に NFC に対してアクティビティが反応してしまいます。
そのため、コード内で動的にインテントフィルターを指定する必要があります。この場合、マニフェストファイルには NFC に関するインテントフィルターは記述しません。

アクティビティ内のコード例(抜粋)は以下となります。

private NfcAdapter mNfcAdapter;
private PendingIntent mPendingIntent;
private IntentFilter[] mIntentFilters;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    〜省略(レイアウトの指定など)〜

    /*
     * Foreground Dispatch の設定
     * このアプリが優先して Intent を受け取れるようにする
     * 実際の設定・解除は onResume と onPause で実行
     */
    mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());

    Intent intent = new Intent(getApplicationContext(), getClass());
    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    mPendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);

    IntentFilter intentFilter = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
    mIntentFilters = new IntentFilter[]{intentFilter};
}

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

    // Resumed に移る前に、 Foreground Dispatch を有効にしておく
    mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, mIntentFilters, null);
}

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

    // Paused に移る前に、 Foreground Dispatch を解除しておく
    mNfcAdapter.disableForegroundDispatch(this);
}

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

    〜 省略(NFC タグ発見時の読み込み or 書き込み処理) 〜
}

このように Foreground Dispatch を設定しておくことで、アクティビティが表示されている場合だけ NFC タグに対する処理を行うことができます。
それ以外の場合に NFC タグを近づけても、別の NFC 対応アプリが反応するようになります。