表題の通りです。
こちらの2つをさっくりまとめた記事になります。
とりあえず端末が読み込んだらアプリを起動させる
直近でNFCを読み取り機能を搭載したアプリを開発することになりました。
AndroidでのNFC読み取りは、ハードウェアがNFCを読み込んだタイミングで暗黙のIntentを投げるので、それを受け取るように設定する必要があります。
<?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>
<?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()
によって取得します。
Intent intent = getIntent();
if (intent != null && NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
// ここで読み取り処理
}
しかしこの設定ではアプリが立ち上がっていない状態でも端末がNFCを読み込んだ瞬間にアプリを呼び出してしまうため、作るアプリの用途によっては不便なことになってしまいます。
特定のActivityでのみ読み取りを実施
では特定のActivityでのみ読み取り機能を立ち上げたい場合どうするかと言うと、Foreground Dispatch Systemと呼ばれる機能を使います。
おおざっぱに解説すると「Intent-filterを動的に設定しつつ、自身のIntentの受け取りを最優先にする」仕組みです。
<?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>
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()で処理をする必要があります。ご注意ください。
現場からは以上です。