LoginSignup
18
29

More than 5 years have passed since last update.

AndroidのNFC読み取りを特定のActivityが立ち上がっている時のみ行う

Posted at

表題の通りです。

こちらの2つをさっくりまとめた記事になります。

とりあえず端末が読み込んだらアプリを起動させる

直近でNFCを読み取り機能を搭載したアプリを開発することになりました。
AndroidでのNFC読み取りは、ハードウェアがNFCを読み込んだタイミングで暗黙のIntentを投げるので、それを受け取るように設定する必要があります。

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest package="io.github.yamacraft.app.nfc"
          xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- NFCの利用許可、2つ目は「NFCがない端末はインストールさせない」設定 -->
    <uses-permission android:name="android.permission.NFC" />
    <uses-feature android:name="android.hardware.nfc" android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".view.activity.MainActivity">
            <!-- NFCの読み取りからの立ち上げを設定 -->
             <intent-filter>
                 <action android:name="android.nfc.action.TECH_DISCOVERED"/>
             </intent-filter>

             <!-- 対象となるNFCフォーマットを指定 -->
             <meta-data android:name="android.nfc.action.TECH_DISCOVERED"
                 android:resource="@xml/nfc_tech_filter" />
        </activity>
    </application>

</manifest>
nfc_tech_filter.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
        <tech>android.nfc.tech.NfcA</tech>
        <tech>android.nfc.tech.NfcB</tech>
        <tech>android.nfc.tech.NfcF</tech>
        <tech>android.nfc.tech.NfcV</tech>
        <tech>android.nfc.tech.Ndef</tech>
        <tech>android.nfc.tech.NdefFormatable</tech>
        <tech>android.nfc.tech.MifareClassic</tech>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
</resources>

こうすることで、各種フォーマットのNFCが端末で読み込まれた時にアプリ(Activity)が呼び出されるようになります。読み込んだNFCの情報はgetIntent()によって取得します。

MainActivity.java
Intent intent = getIntent();
if (intent != null && NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
    // ここで読み取り処理
}

しかしこの設定ではアプリが立ち上がっていない状態でも端末がNFCを読み込んだ瞬間にアプリを呼び出してしまうため、作るアプリの用途によっては不便なことになってしまいます。

特定のActivityでのみ読み取りを実施

では特定のActivityでのみ読み取り機能を立ち上げたい場合どうするかと言うと、Foreground Dispatch Systemと呼ばれる機能を使います。

おおざっぱに解説すると「Intent-filterを動的に設定しつつ、自身のIntentの受け取りを最優先にする」仕組みです。

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest package="io.github.yamacraft.app.nfc"
          xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- NFCの利用許可、2つ目は「NFCがない端末はインストールさせない」設定 -->
    <uses-permission android:name="android.permission.NFC" />
    <uses-feature android:name="android.hardware.nfc" android:required="true" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".view.activity.MainActivity">
            <!-- ここでNFC読み取りからの立ち上げ設定を「しない」 -->
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

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

             <!-- 対象となるNFCフォーマットを指定 -->
             <meta-data android:name="android.nfc.action.TECH_DISCOVERED"
                 android:resource="@xml/nfc_tech_filter" />
        </activity>
    </application>

</manifest>
MainActivity.java

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

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

    // Foreground Dispatch Systemの設定
    mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
    Intent intent = new Intent(getApplicationContext(), getClass());
    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    mPendingIntent = PendingIntent.getActivity(this, 0, intent, 0);

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

@Override
protected void onResume() {
    super.onResume();
    mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, mIntentFilters, null);
}

@Override
protected void onPause() {
    super.onPause();
    mNfcAdapter.disableForegroundDispatch(this);
}

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    if (intent != null && NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
        // ここで読み取り処理
    }
}

ActivityをSingleTopで立ち上げるため、onCreate()ではなくonNewIntent()で処理をする必要があります。ご注意ください。

現場からは以上です。

18
29
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
29