このドキュメントでは、さまざまなタグ テクノロジーの使用、NFC タグへの書き込み、フォアグラウンド ディスパッチなど、NFC の高度なトピックについて説明します。これにより、フォアグラウンドのアプリは、他のアプリが同じインテントをフィルタしても、フォアグラウンドのアプリでインテントを処理できます。
サポートされているタグ テクノロジーを利用する
NFC タグと Android 搭載デバイスを使用する場合、タグのデータの読み取りと書き込みに使用する主な形式は NDEF です。Android では、デバイスが NDEF データを含むタグをスキャンすると、メッセージの解析と NdefMessage
での配信がサポートされます(可能な場合)。ただし、NDEF データを含まないタグをスキャンする場合や、NDEF データを MIME タイプまたは URI にマッピングできない場合もあります。そのような場合は、タグと直接通信を行い、独自のプロトコル(未加工のバイト単位)でタグに対する読み取りと書き込みを行う必要があります。Android では、android.nfc.tech
パッケージを使用してこれらのユースケースの一般的なサポートを提供しています(表 1 をご覧ください)。getTechList()
メソッドを使用して、タグでサポートされているテクノロジーを判別し、android.nfc.tech
によって提供されるいずれかのクラスを使用して、対応する TagTechnology
オブジェクトを作成できます。
クラス | 説明 |
---|---|
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 データおよび NDEF としてフォーマットされた NFC タグのオペレーションへのアクセスを提供します。 |
NdefFormatable |
NDEF にフォーマット可能なタグに対するフォーマット オペレーションを提供します。 |
以下のタグ テクノロジーは、Android 搭載デバイスでサポートされている必要はありません。
クラス | 説明 |
---|---|
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, PendingIntent.FLAG_MUTABLE)
Java
PendingIntent pendingIntent = PendingIntent.getActivity( this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), PendingIntent.FLAG_MUTABLE);
- インターセプトするインテントを処理するインテント フィルタを宣言します。フォアグラウンド ディスパッチ システムは、指定されたインテント フィルタを、デバイスがタグをスキャンしたときに受け取ったインテントを使ってチェックします。一致した場合、アプリはそのインテントを処理します。一致しない場合、フォアグラウンド ディスパッチ システムはインテント ディスパッチ システムにフォールバックします。
インテント フィルタとテクノロジー フィルタの
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()
の呼び出しにより必ず呼び出せます)。スキャンした NFC タグからのデータを処理するためのonNewIntent
コールバックも実装する必要があります。
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 }