このドキュメントでは、さまざまなタグ テクノロジー、NFC タグへの書き込み、フォアグラウンド ディスパッチ(これにより、フォアグラウンドのアプリケーションは、他のアプリケーションが同じインテントをフィルタリングする場合でも、そのインテントを処理できます)など、高度な NFC に関するトピックについて説明します。
サポートされているタグ テクノロジーを利用する
NFC タグや Android デバイスを利用する場合、タグの読み書きに使用する主な形式は NDEF です。デバイスが NDEF データが含まれるタグをスキャンすると、Android は、可能であればメッセージの解析と NdefMessage
への配信をサポートします。ただし、NDEF データを含まないタグをスキャンした場合、または NDEF データを MIME タイプまたは URI にマップできない場合があります。その場合、タグと直接通信し、独自のプロトコルで(生のバイト列で)タグを読み書きする必要があります。Android は、android.nfc.tech
パッケージ(表 1 を参照)を使用してこうした使用例の一般的なサポートを提供します。たとえば、getTechList()
メソッドを使用して、タグでサポートされているテクノロジーを特定し、対応する TagTechnology
オブジェクトを android.nfc.tech
が提供するクラスのいずれかで作成できます。
表 1. サポートされているタグ テクノロジー
クラス | 説明 |
---|---|
TagTechnology |
すべてのタグ テクノロジー クラスで実装する必要があるインターフェースです。 |
NfcA |
NFC-A(ISO 14443-3A)プロパティと I/O オペレーションへのアクセスを提供します。 |
NfcB |
NFC-B(ISO 14443-3B)プロパティと I/O オペレーションへのアクセスを提供します。 |
NfcF |
NFC-F(JIS 6319-4)プロパティと I/O オペレーションへのアクセスを提供します。 |
NfcV |
NFC-V(ISO 15693)プロパティと I/O オペレーションへのアクセスを提供します。 |
IsoDep |
ISO-DEP(ISO 14443-4)プロパティと I/O オペレーションへのアクセスを提供します。 |
Ndef |
NDEF としてフォーマットされた NFC タグの NDEF データとオペレーションへのアクセスを提供します。 |
NdefFormatable |
NDEF にフォーマット可能なタグに対するフォーマット オペレーションを提供します。 |
次のタグ テクノロジーは、Android デバイスでサポートされていなくてもかまいません。
表 2. サポートされているタグ テクノロジー(オプション)
クラス | 説明 |
---|---|
MifareClassic |
当該 Android デバイスが MIFARE をサポートしている場合、MIFARE Classic プロパティと I/O オペレーションへのアクセスを提供します。 |
MifareUltralight |
当該 Android デバイスが MIFARE をサポートしている場合、MIFARE Ultralight プロパティと I/O オペレーションへのアクセスを提供します。 |
タグ テクノロジーと ACTION_TECH_DISCOVERED インテントを利用する
デバイスで NDEF データを含むタグをスキャンしたが MIME または URI にマッピングできなかった場合、タグ ディスパッチ システムは ACTION_TECH_DISCOVERED
インテントを利用してアクティビティを開始しようとします。ACTION_TECH_DISCOVERED
は、NDEF 以外のデータを含むタグをスキャンする場合にも使用されます。このフォールバックがあれば、タグ ディスパッチ システムがデータを解析できなかった場合に、タグのデータを直接処理できます。タグ テクノロジーを利用する際の基本的な手順は次のとおりです。
- 処理するタグ テクノロジーを指定する
ACTION_TECH_DISCOVERED
インテントをフィルタリングします。詳細については、NFC インテントのフィルタリングをご覧ください。一般的に、タグ ディスパッチ システムは、NDEF メッセージを MIME タイプまたは URI にマッピングできない場合、またはスキャンされたタグに NDEF データが含まれていない場合、ACTION_TECH_DISCOVERED
インテントを開始しようとします。この決定方法に関する詳細は、タグ ディスパッチ システムをご覧ください。 - アプリがインテントを受け取ったら、そのインテントから
Tag
オブジェクトを取得します。Kotlin
var tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Java
Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
android.nfc.tech
パッケージに含まれるクラスのget
ファクトリ メソッドのいずれかを呼び出して、TagTechnology
のインスタンスを取得します。サポートされているタグ テクノロジーを列挙するには、get
ファクトリ メソッドを呼び出す前にgetTechList()
を呼び出します。たとえば、Tag
からMifareUltralight
のインスタンスを取得するには、以下のようにします。Kotlin
MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG))
Java
MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));
タグを読み書きする
NFC タグの読み取りと書き込みには、インテントからタグを取得し、タグとの通信を開始します。タグに対してデータを読み書きするには、独自のプロトコル スタックを定義する必要があります。ただし、タグを直接処理する場合は、NDEF データの読み取りと書き込みが可能です。どのように構築するかはユーザー次第です。次の例は、MIFARE Ultralight タグを利用する方法を示しています。
Kotlin
package com.example.android.nfc import android.nfc.Tag import android.nfc.tech.MifareUltralight import java.io.IOException import java.nio.charset.Charset class MifareUltralightTagTester { fun writeTag(tag: Tag, tagText: String) { MifareUltralight.get(tag)?.use { ultralight -> ultralight.connect() Charset.forName("US-ASCII").also { usAscii -> ultralight.writePage(4, "abcd".toByteArray(usAscii)) ultralight.writePage(5, "efgh".toByteArray(usAscii)) ultralight.writePage(6, "ijkl".toByteArray(usAscii)) ultralight.writePage(7, "mnop".toByteArray(usAscii)) } } fun readTag(tag: Tag): String? { return MifareUltralight.get(tag)?.use { mifare -> mifare.connect() val payload = mifare.readPages(4) String(payload, Charset.forName("US-ASCII")) } } } }
Java
package com.example.android.nfc; import android.nfc.Tag; import android.nfc.tech.MifareUltralight; import android.util.Log; import java.io.IOException; import java.nio.charset.Charset; public class MifareUltralightTagTester { private static final String TAG = MifareUltralightTagTester.class.getSimpleName(); public void writeTag(Tag tag, String tagText) { MifareUltralight ultralight = MifareUltralight.get(tag); try { ultralight.connect(); ultralight.writePage(4, "abcd".getBytes(Charset.forName("US-ASCII"))); ultralight.writePage(5, "efgh".getBytes(Charset.forName("US-ASCII"))); ultralight.writePage(6, "ijkl".getBytes(Charset.forName("US-ASCII"))); ultralight.writePage(7, "mnop".getBytes(Charset.forName("US-ASCII"))); } catch (IOException e) { Log.e(TAG, "IOException while writing MifareUltralight...", e); } finally { try { ultralight.close(); } catch (IOException e) { Log.e(TAG, "IOException while closing MifareUltralight...", e); } } } public String readTag(Tag tag) { MifareUltralight mifare = MifareUltralight.get(tag); try { mifare.connect(); byte[] payload = mifare.readPages(4); return new String(payload, Charset.forName("US-ASCII")); } catch (IOException e) { Log.e(TAG, "IOException while reading MifareUltralight message...", e); } finally { if (mifare != null) { try { mifare.close(); } catch (IOException e) { Log.e(TAG, "Error closing tag...", e); } } } return null; } }
フォアグラウンド ディスパッチ システムを使用する
フォアグラウンド ディスパッチ システムにより、アクティビティはインテントをインターセプトし、同じインテントを処理する他のアクティビティよりも優先することができます。このシステムを使用するには、Android システムがアプリケーションに適切なインテントを送信できるように、データ構造をいくつか構築する必要があります。フォアグラウンド ディスパッチ システムを有効にするには:
- アクティビティの
onCreate()
メソッドに次のコードを追加します。PendingIntent
オブジェクトを作成し、Android システムがタグのスキャン時に詳細を入力できるようにします。Kotlin
val intent = Intent(this, javaClass).apply { addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) } var pendingIntent: PendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
Java
PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
- インターセプトするインテントを処理するインテント フィルタを宣言します。フォアグラウンド ディスパッチ システムは、デバイスがタグのスキャン時に受信したインテントを使用して、指定されたインテント フィルタをチェックします。一致した場合、アプリはそのインテントを処理します。一致しない場合、フォアグラウンド ディスパッチ システムはインテント ディスパッチ システムにフォールバックします。
インテント フィルタとテクノロジー フィルタの
null
配列を指定すると、TAG_DISCOVERED
インテントにフォールバックするタグをすべてフィルタすることになります。以下のコード スニペットは、NDEF_DISCOVERED
の MIME タイプをすべて処理します。必要なものだけを処理する必要があります。Kotlin
val ndef = IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED).apply { try { addDataType("*/*") /* Handles all MIME based dispatches. You should specify only the ones that you need. */ } catch (e: IntentFilter.MalformedMimeTypeException) { throw RuntimeException("fail", e) } } intentFiltersArray = arrayOf(ndef)
Java
IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); try { ndef.addDataType("*/*"); /* Handles all MIME based dispatches. You should specify only the ones that you need. */ } catch (MalformedMimeTypeException e) { throw new RuntimeException("fail", e); } intentFiltersArray = new IntentFilter[] {ndef, };
- アプリケーションで処理するタグ テクノロジーの配列を設定します。
Object.class.getName()
メソッドを呼び出して、サポートするテクノロジーのクラスを取得します。Kotlin
techListsArray = arrayOf(arrayOf<String>(NfcF::class.java.name))
Java
techListsArray = new String[][] { new String[] { NfcF.class.getName() } };
- 次のアクティビティ ライフサイクル コールバックをオーバーライドし、アクティビティがフォーカスを失って(
onPause()
)回復した(onResume()
)ときにフォアグラウンド ディスパッチを有効化および無効化するロジックを追加します。enableForegroundDispatch()
はメインスレッドから、アクティビティがフォアグラウンドにある場合にのみ呼び出す必要があります(これは、onResume()
を呼び出すと保証されます)。また、onNewIntent
コールバックを実装してスキャンした NFC タグのデータを処理する必要があります。
Kotlin
public override fun onPause() { super.onPause() adapter.disableForegroundDispatch(this) } public override fun onResume() { super.onResume() adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray) } public override fun onNewIntent(intent: Intent) { val tagFromIntent: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG) //do something with tagFromIntent }
Java
public void onPause() { super.onPause(); adapter.disableForegroundDispatch(this); } public void onResume() { super.onResume(); adapter.enableForegroundDispatch(this, pendingIntent, intentFiltersArray, techListsArray); } public void onNewIntent(Intent intent) { Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); //do something with tagFromIntent }
完全なサンプルについては、API デモの ForegroundDispatch サンプルをご覧ください。