このドキュメントでは、Android で行う基本的な NFC タスクについて説明します。NDEF メッセージの形式で NFC データを送受信する方法と、これらの機能をサポートする Android フレームワーク API について説明します。NDEF 以外のデータの処理方法など、詳細なトピックについては、高度な NFC をご覧ください。
NDEF データと Android を使用する場合、主に次の 2 つの使用例があります。
- NDEF データを NFC タグから読み取る
- Android ビーム™ を使用してデバイス間で NDEF メッセージをビームする
NFC タグからの NDEF データの読み取りは、タグ ディスパッチ システムで処理されます。このシステムは、検出された NFC タグを分析し、データを適切に分類して、分類されたデータに関連したアプリケーションを起動します。スキャンされた NFC タグを処理するアプリケーションでインテント フィルタを宣言して、データの処理をリクエストできます。
Android ビーム™ 機能により、デバイスを同時にタップして、一方のデバイスからもう一方へ NDEF メッセージをプッシュできます。NFC では手動のデバイス検出やペア設定が不要なため、こうしたインタラクションにより Bluetooth などの他の無線技術よりも簡単にデータを送信できます。2 つのデバイスが範囲内に入ると、接続が自動的に開始されます。Android ビームは NFC API セットを通じて利用できるため、どのアプリケーションでもデバイス間で情報を転送できます。たとえば、連絡先、ブラウザ、YouTube アプリでは Android ビームを使用して、連絡先、ウェブページ、動画を他のデバイスと共有できます。
タグ ディスパッチ システム
Android デバイスは通常、デバイスの設定メニューで NFC が無効になっていない限り、画面のロックが解除されたときに NFC タグを探します。Android デバイスが NFC タグを検出した場合、ユーザーに使用するアプリケーションを確認せずに、最も適切なアクティビティにインテントを処理させることが、期待される動作です。デバイスは NFC タグを至近距離でスキャンするため、ユーザーが手動でアクティビティを選択すると、デバイスがタグから離れて接続が切断される可能性があります。選択ダイアログが表示されないようにするには、アクティビティで重要な NFC タグのみを処理するようにアクティビティを開発する必要があります。
このために、Android には特別なタグ ディスパッチ システムが用意されており、スキャンされた NFC タグを分析、パースし、スキャンされたデータに関連のあるアプリケーションを見つけようとします。これは次の方法で行います。
- NFC タグをパースし、タグにあるデータ ペイロードを識別する MIME タイプや URI を見つけ出します。
- MIME タイプまたは URI、およびペイロードをインテントにカプセル化します。これらの最初 2 つの手順は、NFC タグが MIME タイプと URI にマッピングされる仕組みで説明します。
- インテントに基づいてアクティビティを開始します。これについては、NFC タグがアプリケーションにディスパッチされる仕組みをご覧ください。
NFC タグが MIME タイプと URI にマッピングされる仕組み
NFC アプリケーションの作成を開始する前に、NFC タグの種類、タグ ディスパッチ システムによる NFC タグのパース方法、タグ ディスパッチ システムが NDEF メッセージを検出したときの特別な処理について理解しておく必要があります。NFC タグは幅広いテクノロジーに対応しており、さまざまな方法でデータを書き込むことができます。Android は NFC フォーラムで定義されている NDEF 標準規格を最大限にサポートしています。
NDEF データは、1 つまたは複数のレコード(NdefRecord
)を含むメッセージ(NdefMessage
)内にカプセル化されます。各 NDEF レコードは、作成するレコードタイプの仕様に従って適切な形式にする必要があります。Android では、NDEF データを含まない他の種類のタグもサポートされています。これらのタグは、android.nfc.tech
パッケージに含まれるクラスを使用して処理できます。これらの技術の詳細については、高度な NFC のトピックをご覧ください。このような他の種類のタグを処理する場合は、独自のプロトコル スタックを作成してタグと通信する必要があります。そのため、可能な限り NDEF を使用して、開発を容易にし、Android デバイスを最大限にサポートすることをおすすめします。
注: 完全な NDEF 仕様をダウンロードするには、NFC フォーラムの仕様とアプリケーション ドキュメントのサイトをご覧ください。NDEF レコードの作成例については、一般的な NDEF レコードタイプの作成をご覧ください。
NFC タグの技術的背景について説明したので、次のセクションでは、Android が NDEF 形式のタグを処理する方法について詳しく説明します。Android デバイスが NDEF 形式のデータを含む NFC タグをスキャンすると、メッセージをパースし、データの MIME タイプを見つけ出すか、URI を識別しようとします。これを行うために、システムは NdefMessage
内の最初の NdefRecord
を読み取り、NDEF メッセージ全体(NDEF メッセージには複数の NDEF レコードが含まれる場合があります)の解釈方法を決定します。適切な NDEF 形式メッセージには、最初の NdefRecord
に次のフィールドが含まれます。
- 3 ビットの TNF(Type Name Format)
- 可変長タイプのフィールドの解釈方法を示します。有効な値については、表 1 をご覧ください。
- 可変長タイプ
- レコードのタイプを記述します。
TNF_WELL_KNOWN
を使用している場合、このフィールドを使用してレコードタイプ定義(RTD)を指定します。有効な RTD 値は表 2 に記載されています。 - 可変長 ID
- 当該レコードの一意の識別子。このフィールドは頻繁に使用されることはありませんが、タグを一意に識別する必要がある場合、タグの ID を作成できます。
- 可変長ペイロード
- 読み書きする実際のデータ ペイロード。NDEF メッセージに複数の NDEF レコードを含めることができるため、NDEF メッセージの最初の NDEF レコードに完全なペイロードがあるとは限りません。
タグ ディスパッチ システムは TNF とタイプ フィールドを使用して、MIME タイプまたは URI を NDEF メッセージにマッピングしようと試みます。成功した場合は、その情報を実際のペイロードとともに ACTION_NDEF_DISCOVERED
インテント内にカプセル化します。ただし、タグ ディスパッチ システムが、最初の NDEF レコードに基づいてデータの種類を判別できない場合もあります。これは、NDEF データを MIME タイプや URI にマッピングできない場合や、初めから NFC タグに NDEF データが含まれていない場合に発生します。このような場合、タグのテクノロジーとペイロードに関する情報が含まれる Tag
オブジェクトが代わりに ACTION_TECH_DISCOVERED
インテント内にカプセル化されます。
表 1 は、タグ ディスパッチ システムが TNF とタイプ フィールドを MIME タイプまたは URI にマッピングする方法を示します。MIME タイプまたは URI にマッピングできない TNF についても記載されています。この場合、タグ ディスパッチ システムは ACTION_TECH_DISCOVERED
にフォールバックします。
たとえば、タグ ディスパッチ システムが TNF_ABSOLUTE_URI
タイプのレコードを検出した場合、そのレコードの可変長タイプ フィールドを URI にマッピングします。タグ ディスパッチ システムは、その URI をペイロードなどのタグに関する他の情報とともに、ACTION_NDEF_DISCOVERED
インテントのデータ フィールドにカプセル化します。一方、TNF_UNKNOWN
タイプのレコードが見つかった場合は、代わりにタグのテクノロジーをカプセル化するインテントを作成します。
表 1. サポートされている TNF とそのマッピング
Type Name Format(TNF) | マッピング |
---|---|
TNF_ABSOLUTE_URI |
タイプ フィールドに基づく URI。 |
TNF_EMPTY |
ACTION_TECH_DISCOVERED にフォールバックします。 |
TNF_EXTERNAL_TYPE |
タイプ フィールドの URN に基づく URI。URN は NDEF タイプ フィールドに短縮形式(<domain_name>:<service_name> )でエンコードされます。Android はこれを vnd.android.nfc://ext/<domain_name>:<service_name> の形式の URI にマッピングします。 |
TNF_MIME_MEDIA |
タイプ フィールドに基づく MIME タイプ。 |
TNF_UNCHANGED |
最初のレコードが無効なため、ACTION_TECH_DISCOVERED にフォールバックします。 |
TNF_UNKNOWN |
ACTION_TECH_DISCOVERED にフォールバックします。 |
TNF_WELL_KNOWN |
レコードタイプ定義(RTD)に応じた MIME タイプまたは URI。RTD はタイプ フィールドに設定されています。利用可能な RTD とそのマッピングの詳細については、表 2 をご覧ください。 |
表 2. TNF_WELL_KNOWN でサポートされている RTD とそのマッピング
レコードタイプ定義(RTD) | マッピング |
---|---|
RTD_ALTERNATIVE_CARRIER |
ACTION_TECH_DISCOVERED にフォールバックします。 |
RTD_HANDOVER_CARRIER |
ACTION_TECH_DISCOVERED にフォールバックします。 |
RTD_HANDOVER_REQUEST |
ACTION_TECH_DISCOVERED にフォールバックします。 |
RTD_HANDOVER_SELECT |
ACTION_TECH_DISCOVERED にフォールバックします。 |
RTD_SMART_POSTER |
ペイロードのパースに基づく URI。 |
RTD_TEXT |
text/plain の MIME タイプ。 |
RTD_URI |
ペイロードに基づく URI。 |
NFC タグがアプリケーションにディスパッチされる仕組み
タグ ディスパッチ システムが、NFC タグとその識別情報をカプセル化するインテントの作成を完了すると、インテントをフィルタする関連アプリケーションにインテントを送信します。複数のアプリケーションがインテントを処理できる場合は、選択ダイアログが表示されるため、ユーザーがアクティビティを選択できます。タグ ディスパッチ システムは、3 つのインテントを定義します。このインテントは、優先度の高い順にリストされます。
-
ACTION_NDEF_DISCOVERED
: NDEF ペイロードを含むタグがスキャンされ、タイプが認識済みのものである場合は、このインテントを使用してアクティビティを開始します。これは最も優先度の高いインテントであり、タグ ディスパッチ システムは、可能な限り他のインテントよりも先にこのインテントでアクティビティを開始しようとします。 ACTION_TECH_DISCOVERED
:ACTION_NDEF_DISCOVERED
インテントを処理するアクティビティが登録されていない場合、タグ ディスパッチ システムはこのインテントでアプリケーションを起動しようとします。スキャンされるタグに MIME タイプまたは URI にマッピングできない NDEF データが含まれている場合、または NDEF データが含まれていなくても既知のテクノロジーのタグである場合、このインテントは(最初にACTION_NDEF_DISCOVERED
を開始せずに)直接開始されます。ACTION_TAG_DISCOVERED
: このインテントは、ACTION_NDEF_DISCOVERED
またはACTION_TECH_DISCOVERED
インテントを処理するアクティビティがない場合に開始されます。
タグ ディスパッチ システムの基本的な仕組みは次のとおりです。
- NFC タグのパース時にタグ ディスパッチ システムによって作成されたインテント(
ACTION_NDEF_DISCOVERED
またはACTION_TECH_DISCOVERED
)でアクティビティの開始を試みます。 - そのインテントにアクティビティ フィルタがない場合は、次に優先度の低いインテント(
ACTION_TECH_DISCOVERED
またはACTION_TAG_DISCOVERED
)でアクティビティを開始しようとします。これは、アプリケーションがインテントをフィルタするまで、またはタグ ディスパッチ システムが可能なすべてのインテントを試みるまで行います。 - アプリケーションがどのインテントもフィルタしない場合は、何もしません。

可能な限り、NDEF メッセージと ACTION_NDEF_DISCOVERED
インテントを処理します。これは、3 つの中で最も具体的であるためです。このインテントを使用すると、他の 2 つのインテントよりも適切なタイミングでアプリケーションを起動できるため、ユーザーのエクスペリエンスが向上します。
Android マニフェストで NFC アクセスをリクエストする
デバイスの NFC ハードウェアにアクセスし、NFC インテントを適切に処理する前に、AndroidManifest.xml
ファイルで次の項目を宣言します。
- NFC ハードウェアにアクセスするための NFC
<uses-permission>
要素:<uses-permission android:name="android.permission.NFC" />
- アプリケーションでサポート可能な SDK の最小バージョン。API レベル 9 では、
ACTION_TAG_DISCOVERED
による限定的なタグ ディスパッチのみをサポートし、EXTRA_NDEF_MESSAGES
による NDEF メッセージへのアクセスのみを提供します。他のタグプロパティや I/O オペレーションにアクセスすることはできません。API レベル 10 には、リーダー / ライターの包括的サポートとフォアグラウンド NDEF プッシュ機能があります。API レベル 14 には、Android ビームで NDEF メッセージを他のデバイスにプッシュする簡単な方法と、NDEF レコードを作成するための便利な追加メソッドがあります。<uses-sdk android:minSdkVersion="10"/>
uses-feature
要素を使用すると、NFC ハードウェアを搭載したデバイスでのみアプリケーションが Google Play に表示されます。<uses-feature android:name="android.hardware.nfc" android:required="true" />
NFC 機能を使用していても、その機能がアプリケーションにとって重要ではない場合は、
uses-feature
要素を省略できます。getDefaultAdapter()
がnull
であるかどうかを確認することで、NFC の有効性を実行時にチェックできます。
NFC インテントのフィルタ
処理したい NFC タグがスキャンされたときにアプリケーションを起動するためには、1 つ、2 つ、または 3 つすべての NFC インテントを Android マニフェスト内でフィルタできます。ただし、通常は、アプリケーションの起動タイミングを最大限に制御するために、ACTION_NDEF_DISCOVERED
インテントをフィルタする必要があります。ACTION_TECH_DISCOVERED
インテントは、ACTION_NDEF_DISCOVERED
をフィルタしているアプリケーションがない場合、またはペイロードが NDEF でない場合の ACTION_NDEF_DISCOVERED
のフォールバックです。通常、ACTION_TAG_DISCOVERED
のフィルタは、カテゴリのフィルタとしては一般的すぎます。多くのアプリケーションは、ACTION_TAG_DISCOVERED
の前に ACTION_NDEF_DISCOVERED
または ACTION_TECH_DISCOVERED
をフィルタするため、アプリケーションが起動する可能性は低くなります。ACTION_TAG_DISCOVERED
は、ACTION_NDEF_DISCOVERED
または ACTION_TECH_DISCOVERED
インテントを処理する他のアプリケーションがインストールされていない場合の最後の手段としてのみ利用できます。
NFC タグのデプロイメントはさまざまであり、多くの場合制御できず、これが常に可能であるとは限らないため、必要に応じて他の 2 つのインテントにフォールバックできます。タグのタイプと書き込みデータを制御できる場合は、NDEF を使用してタグをフォーマットすることをおすすめします。次のセクションでは、インテントのタイプごとにフィルタを適用する方法について説明します。
ACTION_NDEF_DISCOVERED
ACTION_NDEF_DISCOVERED
インテントをフィルタするには、フィルタ対象のデータタイプとともにインテント フィルタを宣言します。MIME タイプが text/plain
の ACTION_NDEF_DISCOVERED
インテントをフィルタする例を以下に示します。
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain" /> </intent-filter>
次の例では、URI を https://developer.android.com/index.html
の形式でフィルタしています。
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:scheme="http" android:host="developer.android.com" android:pathPrefix="/index.html" /> </intent-filter>
ACTION_TECH_DISCOVERED
アクティビティで ACTION_TECH_DISCOVERED
インテントをフィルタする場合、XML リソース ファイル(tech-list
セット内でアクティビティがサポートするテクノロジーを指定します)を作成する必要があります。tech-list
セットがタグでサポートされているテクノロジー(getTechList()
の呼び出しで取得可能)のサブセットである場合、アクティビティは一致すると見なされます。
たとえば、スキャンされるタグが MifareClassic、NdefFormatable、NfcA をサポートしている場合、アクティビティを一致させるには、1 つ、2 つ、または 3 つすべてのテクノロジー(他のものは含めない)を tech-list
セットで指定する必要があります。
次の例では、テクノロジーをすべて定義しています。必要のないものは削除できます。このファイル(名前は自由に指定できます)を <project-root>/res/xml
フォルダに保存します。
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <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>
また、複数の tech-list
セットを指定できます。各 tech-list
セットは個別に扱われ、getTechList()
で返されるテクノロジーのサブセットに相当する tech-list
セットが 1 つでも存在する場合、アクティビティは一致すると見なされます。これにより、テクノロジーの照合に AND
と OR
のセマンティクスが適用されます。次の例は、NfcA と Ndef テクノロジー、または NfcB と Ndef テクノロジーをサポートできるタグに一致します。
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <tech-list> <tech>android.nfc.tech.NfcA</tech> <tech>android.nfc.tech.Ndef</tech> </tech-list> <tech-list> <tech>android.nfc.tech.NfcB</tech> <tech>android.nfc.tech.Ndef</tech> </tech-list> </resources>
次の例のように、AndroidManifest.xml
ファイルで、<activity>
要素内の <meta-data>
要素に、作成したばかりのリソース ファイルを指定します。
<activity> ... <intent-filter> <action android:name="android.nfc.action.TECH_DISCOVERED"/> </intent-filter> <meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" /> ... </activity>
タグ テクノロジーの処理方法と ACTION_TECH_DISCOVERED
インテントの詳細については、高度な NFC に関するドキュメントのサポートされているタグ テクノロジーの処理をご覧ください。
ACTION_TAG_DISCOVERED
ACTION_TAG_DISCOVERED
をフィルタするには、次のインテント フィルタを使用します。
<intent-filter> <action android:name="android.nfc.action.TAG_DISCOVERED"/> </intent-filter>
インテントから情報を取得する
NFC インテントに起因してアクティビティが開始された場合、スキャンされた NFC タグに関する情報をインテントから取得できます。インテントには、スキャンされたタグに応じて次の EXTRA 項目が含まれます。
EXTRA_TAG
(必須): スキャンされたタグを表すTag
オブジェクト。EXTRA_NDEF_MESSAGES
(任意): タグからパースされた NDEF メッセージの配列。この EXTRA はACTION_NDEF_DISCOVERED
インテントに必須です。EXTRA_ID
(任意): タグの低レベル ID。
これらの EXTRA を取得するには、アクティビティが NFC インテントの 1 つで起動されたかどうかを確認し、タグがスキャンされたことを確認してから、インテントから EXTRA を取得します。ACTION_NDEF_DISCOVERED
インテントを確認し、インテントの EXTRA から NDEF メッセージを取得する例を次に示します。
Kotlin
override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) ... if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) { intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMessages -> val messages: List<NdefMessage> = rawMessages.map { it as NdefMessage } // Process the messages array. ... } } }
Java
@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); ... if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) { Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); if (rawMessages != null) { NdefMessage[] messages = new NdefMessage[rawMessages.length]; for (int i = 0; i < rawMessages.length; i++) { messages[i] = (NdefMessage) rawMessages[i]; } // Process the messages array. ... } } }
または、インテントから Tag
オブジェクトを取得できます。これにはペイロードが含まれ、タグのテクノロジーを列挙することができます。
Kotlin
val tag: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
Java
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
一般的なタイプの NDEF レコードを作成する
ここでは、NFC タグへの書き込みや Android ビームを使用したデータ送信に役立つ、一般的なタイプの NDEF レコードを作成する方法について説明します。Android 4.0(API レベル 14)以降では、URI レコードの自動作成に createUri()
メソッドを使用できます。Android 4.1(API レベル 16)以降、createExternal()
と createMime()
を使用して MIME および外部タイプの NDEF レコードを作成できます。NDEF レコードを手動で作成する際の間違いを避けるため、可能な限りこれらのヘルパー メソッドを使用してください。
このセクションでは、レコードに対応するインテント フィルタの作成方法についても説明します。これらの NDEF レコードの例はすべて、NDEF メッセージ(タグに書き込むかビームする)の最初の NDEF レコードに含まれている必要があります。
TNF_ABSOLUTE_URI
注: TNF_ABSOLUTE_URI
の代わりに RTD_URI
の使用をおすすめします。こちらの方がより効率的です。
TNF_ABSOLUTE_URI
NDEF レコードの作成方法を以下に示します。
Kotlin
val uriRecord = ByteArray(0).let { emptyByteArray -> NdefRecord( TNF_ABSOLUTE_URI, "https://developer.android.com/index.html".toByteArray(Charset.forName("US-ASCII")), emptyByteArray, emptyByteArray ) }
Java
NdefRecord uriRecord = new NdefRecord( NdefRecord.TNF_ABSOLUTE_URI , "https://developer.android.com/index.html".getBytes(Charset.forName("US-ASCII")), new byte[0], new byte[0]);
前の NDEF レコードのインテント フィルタは次のようになります。
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="http" android:host="developer.android.com" android:pathPrefix="/index.html" /> </intent-filter>
TNF_MIME_MEDIA
TNF_MIME_MEDIA
NDEF レコードの作成方法を以下に示します。
createMime()
メソッドを使用:
Kotlin
val mimeRecord = NdefRecord.createMime( "application/vnd.com.example.android.beam", "Beam me up, Android".toByteArray(Charset.forName("US-ASCII")) )
Java
NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam", "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));
手動で NdefRecord
を作成:
Kotlin
val mimeRecord = Charset.forName("US-ASCII").let { usAscii -> NdefRecord( NdefRecord.TNF_MIME_MEDIA, "application/vnd.com.example.android.beam".toByteArray(usAscii), ByteArray(0), "Beam me up, Android!".toByteArray(usAscii) ) }
Java
NdefRecord mimeRecord = new NdefRecord( NdefRecord.TNF_MIME_MEDIA , "application/vnd.com.example.android.beam".getBytes(Charset.forName("US-ASCII")), new byte[0], "Beam me up, Android!".getBytes(Charset.forName("US-ASCII")));
前の NDEF レコードのインテント フィルタは次のようになります。
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="application/vnd.com.example.android.beam" /> </intent-filter>
TNF_WELL_KNOWN(RTD_TEXT)
TNF_WELL_KNOWN
NDEF レコードの作成方法を以下に示します。
Kotlin
fun createTextRecord(payload: String, locale: Locale, encodeInUtf8: Boolean): NdefRecord { val langBytes = locale.language.toByteArray(Charset.forName("US-ASCII")) val utfEncoding = if (encodeInUtf8) Charset.forName("UTF-8") else Charset.forName("UTF-16") val textBytes = payload.toByteArray(utfEncoding) val utfBit: Int = if (encodeInUtf8) 0 else 1 shl 7 val status = (utfBit + langBytes.size).toChar() val data = ByteArray(1 + langBytes.size + textBytes.size) data[0] = status.toByte() System.arraycopy(langBytes, 0, data, 1, langBytes.size) System.arraycopy(textBytes, 0, data, 1 + langBytes.size, textBytes.size) return NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, ByteArray(0), data) }
Java
public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) { byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII")); Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16"); byte[] textBytes = payload.getBytes(utfEncoding); int utfBit = encodeInUtf8 ? 0 : (1 << 7); char status = (char) (utfBit + langBytes.length); byte[] data = new byte[1 + langBytes.length + textBytes.length]; data[0] = (byte) status; System.arraycopy(langBytes, 0, data, 1, langBytes.length); System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length); NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data); return record; }
前の NDEF レコードのインテント フィルタは次のようになります。
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter>
TNF_WELL_KNOWN(RTD_URI)
TNF_WELL_KNOWN
NDEF レコードの作成方法を以下に示します。
createUri(String)
メソッドを使用:
Kotlin
val rtdUriRecord1 = NdefRecord.createUri("http://example.com")
Java
NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com");
createUri(Uri)
メソッドを使用:
Kotlin
val rtdUriRecord2 = Uri.parse("http://example.com").let { uri -> NdefRecord.createUri(uri) }
Java
Uri uri = Uri.parse("http://example.com"); NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);
手動で NdefRecord
を作成:
Kotlin
val uriField = "example.com".toByteArray(Charset.forName("US-ASCII")) val payload = ByteArray(uriField.size + 1) //add 1 for the URI Prefix payload [0] = 0x01 //prefixes http://www. to the URI System.arraycopy(uriField, 0, payload, 1, uriField.size) //appends URI to payload val rtdUriRecord = NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, ByteArray(0), payload)
Java
byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII")); byte[] payload = new byte[uriField.length + 1]; //add 1 for the URI Prefix payload[0] = 0x01; //prefixes http://www. to the URI System.arraycopy(uriField, 0, payload, 1, uriField.length); //appends URI to payload NdefRecord rtdUriRecord = new NdefRecord( NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload);
前の NDEF レコードのインテント フィルタは次のようになります。
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="http" android:host="example.com" android:pathPrefix="" /> </intent-filter>
TNF_EXTERNAL_TYPE
TNF_EXTERNAL_TYPE
NDEF レコードの作成方法を以下に示します。
createExternal()
メソッドを使用:
Kotlin
var payload: ByteArray //assign to your data val domain = "com.example" //usually your app's package name val type = "externalType" val extRecord = NdefRecord.createExternal(domain, type, payload)
Java
byte[] payload; //assign to your data String domain = "com.example"; //usually your app's package name String type = "externalType"; NdefRecord extRecord = NdefRecord.createExternal(domain, type, payload);
手動で NdefRecord
を作成:
Kotlin
var payload: ByteArray ... val extRecord = NdefRecord( NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType".toByteArray(Charset.forName("US-ASCII")), ByteArray(0), payload )
Java
byte[] payload; ... NdefRecord extRecord = new NdefRecord( NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType".getBytes(Charset.forName("US-ASCII")), new byte[0], payload);
前の NDEF レコードのインテント フィルタは次のようになります。
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="vnd.android.nfc" android:host="ext" android:pathPrefix="/com.example:externalType"/> </intent-filter>
TNF_EXTERNAL_TYPE
を使用すると、より一般的な NFC タグをデプロイし、Android デバイスと Android 以外のデバイスの両方をより適切にサポートできます。
注: TNF_EXTERNAL_TYPE
の URN の標準形式は urn:nfc:ext:example.com:externalType
ですが、NFC フォーラムの RTD 仕様では、URN の urn:nfc:ext:
部分を NDEF レコードから削除する必要があると宣言されています。したがって、必要となるのは、ドメイン(例では example.com
)とタイプ(例では externalType
)をコロンで区切って指定することだけです。TNF_EXTERNAL_TYPE
をディスパッチすると、Android は urn:nfc:ext:example.com:externalType
URN を vnd.android.nfc://ext/example.com:externalType
URI に変換します。これは、この例のインテント フィルタで宣言されているとおりです。
Android アプリのレコード
Android 4.0(API レベル 14)で導入された Android Application Record(AAR)により、NFC タグのスキャン時にアプリケーションが起動される確実性が向上します。AAR には、NDEF レコード内に埋め込まれたアプリケーションのパッケージ名があります。Android は NDEF メッセージ全体で AAR を検索するため、NDEF メッセージの任意の NDEF レコードに AAR を追加できます。AAR が見つかると、AAR 内のパッケージ名に基づいてアプリケーションを起動します。アプリケーションがデバイスにない場合、Google Play を起動してアプリをダウンロードします。
AAR は、同じインテントをフィルタリングする他のアプリケーションにより、自分がデプロイした特定のタグが処理されるのを防ぐ場合に便利です。AAR は、パッケージ名の制約のため、アプリケーション レベルでのみサポートされ、インテント フィルタリングの場合のようにアクティビティ レベルではサポートされません。アクティビティ レベルでインテントを処理する場合は、インテント フィルタを使用します。
タグに AAR が含まれている場合、タグ ディスパッチ システムによるディスパッチ方法は以下のとおりです。
- 通常どおりに、インテント フィルタを使用したアクティビティの開始を試みます。インテントに一致するアクティビティが AAR にも一致する場合、アクティビティを開始します。
- インテントをフィルタするアクティビティが AAR と一致しない場合、複数のアクティビティがインテントを処理できる場合、インテントを処理するアクティビティがない場合は、AAR で指定されたアプリを起動します。
- AAR で開始できるアプリがない場合は、Google Play にアクセスして AAR ベースのアプリをダウンロードします。
注: AAR とインテント ディスパッチ システムを、フォアグラウンド ディスパッチ システムでオーバーライドできます。これにより、NFC タグが検出されたときにフォアグラウンド アクティビティを優先させることができます。この方法で AAR とインテント ディスパッチ システムをオーバーライドするには、アクティビティがフォアグラウンドにある必要があります。
AAR を含まないスキャンされたタグをフィルタする場合は、通常どおりインテント フィルタを宣言できます。これは、アプリケーションが AAR を含まない他のタグと関連している場合に便利です。たとえば、サードパーティがデプロイする一般的なタグだけでなく、自身がデプロイする独自のタグをアプリケーションで処理できるよう保証する場合などです。AAR は Android 4.0 以降のデバイスに固有であるため、タグをデプロイする場合は AAR と、MIME タイプまたは URI を組み合わせて使用し、幅広いデバイスをサポートすることをおすすめします。さらに、NFC タグをデプロイする際は、NFC タグの書き込み方法を検討して、ほとんどのデバイス(Android デバイスやその他のデバイス)への対応を可能にしてください。これを行うには、重複が比較的生じにくい MIME タイプや URI を定義して、アプリケーションが識別しやすいようにします。
Android は AAR を作成するシンプルな API createApplicationRecord()
を提供します。AAR を NdefMessage
の任意の場所に埋め込むだけですみます。AAR が NdefMessage
内の唯一のレコードでない限り、NdefMessage
の最初のレコードを使用する必要はありません。これは、Android システムが NdefMessage
の最初のレコードをチェックして、タグの MIME タイプまたは URI(アプリケーションがフィルタするインテントの作成に使用されます)を決定するためです。AAR を作成する方法を次のコードで示します。
Kotlin
val msg = NdefMessage( arrayOf( ..., NdefRecord.createApplicationRecord("com.example.android.beam") ) )
Java
NdefMessage msg = new NdefMessage( new NdefRecord[] { ..., NdefRecord.createApplicationRecord("com.example.android.beam")} ); )
NDEF メッセージを他のデバイスにビームする
Android ビームを使用すると、2 台の Android デバイス間でシンプルなピアツーピアのデータ交換が可能になります。別のデバイスにデータをビームするアプリケーションがフォアグラウンドで実行していること、およびデータを受信するデバイスではロックを解除することが必要です。ビーム送信側デバイスと受信側デバイスが十分に接近すると、送信側デバイスの UI に [タップしてビーム] が表示されます。ユーザーは受信側デバイスにメッセージをビームするかどうかを選択できます。
注: フォアグラウンド NDEF プッシュは API レベル 10 で利用可能であり、Android ビームと同様の機能を提供します。これらの API のサポートは終了しましたが、古いデバイスへの対応に使用できます。詳しくは、enableForegroundNdefPush()
をご覧ください。
アプリケーションの Android ビームを有効にするには、次の 2 つのメソッドのいずれかを呼び出します。
setNdefPushMessage()
:NdefMessage
を受け入れて、ビームするメッセージとして設定します。2 つのデバイスが十分に近接したときに自動的にメッセージをビームします。setNdefPushMessageCallback()
:createNdefMessage()
(デバイスがデータをビームする範囲内にあるときに呼び出されます)を含むコールバックを受け入れます。コールバックを使用すると、必要な場合にのみ NDEF メッセージを作成できます。
アクティビティは一度に 1 つの NDEF メッセージしかプッシュできないため、setNdefPushMessage()
よりも setNdefPushMessageCallback()
が優先されます(両方が設定されている場合)。Android ビームを使用するには、次の一般的なガイドラインを満たす必要があります。
- データをビームするアクティビティは、フォアグラウンドでなければなりません。送受信デバイスの両方の画面ロックが解除されている必要があります。
- ビームしているデータを
NdefMessage
オブジェクトにカプセル化する必要があります。 - ビームされたデータを受信する NFC デバイスでは、
com.android.npp
NDEF プッシュ プロトコルまたは NFC フォーラムの SNEP(Simple NDEF Exchange Protocol)をサポートする必要があります。com.android.npp
プロトコルは、API レベル 9(Android 2.3)から API レベル 13(Android 3.2)のデバイスに必須です。API レベル 14(Android 4.0)以降ではcom.android.npp
と SNEP の両方が必須です。
注: Android ビームを有効にしたアクティビティがフォアグラウンドにある場合、標準のインテント ディスパッチ システムが無効になります。ただし、アクティビティでフォアグラウンド ディスパッチも有効にしている場合、フォアグラウンド ディスパッチで設定されたインテント フィルタに一致するタグをスキャンできます。
Android ビームを有効にするには:
- 他のデバイスにプッシュする
NdefRecord
を含むNdefMessage
を作成します。 - アクティビティの
onCreate()
メソッドで、NdefMessage
を使用してsetNdefPushMessage()
を呼び出すか、NfcAdapter.CreateNdefMessageCallback
オブジェクトを渡してsetNdefPushMessageCallback
を呼び出します。これらのメソッドには、Android ビームで有効にするアクティビティ(最低 1 つ)と、アクティブにする他のアクティビティのオプション リストが必要です。一般に、2 つのデバイスが通信範囲内にあるときに、アクティビティが常に同じ NDEF メッセージをプッシュすればよい場合は、
setNdefPushMessage()
を使用します。setNdefPushMessageCallback
を使用するのは、アプリケーションが現在のコンテキストを考慮し、ユーザーのアプリケーションでの操作に応じて NDEF メッセージをプッシュする場合です。
単純なアクティビティの onCreate()
メソッドで NfcAdapter.CreateNdefMessageCallback
を呼び出す例を以下に示します(完全なサンプルについては、AndroidBeamDemo をご覧ください)。この例には、MIME レコードの作成に便利なメソッドもあります。
Kotlin
package com.example.android.beam import android.app.Activity import android.content.Intent import android.nfc.NdefMessage import android.nfc.NdefRecord import android.nfc.NfcAdapter import android.nfc.NfcAdapter.CreateNdefMessageCallback import android.nfc.NfcEvent import android.os.Bundle import android.os.Parcelable import android.widget.TextView import android.widget.Toast import java.nio.charset.Charset class Beam : Activity(), NfcAdapter.CreateNdefMessageCallback { private var nfcAdapter: NfcAdapter? = null private lateinit var textView: TextView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.main) textView = findViewById(R.id.textView) // Check for available NFC Adapter nfcAdapter = NfcAdapter.getDefaultAdapter(this) if (nfcAdapter == null) { Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show() finish() return } // Register callback nfcAdapter?.setNdefPushMessageCallback(this, this) } override fun createNdefMessage(event: NfcEvent): NdefMessage { val text = "Beam me up, Android!\n\n" + "Beam Time: " + System.currentTimeMillis() return NdefMessage( arrayOf( createMime("application/vnd.com.example.android.beam", text.toByteArray()) ) /** * The Android Application Record (AAR) is commented out. When a device * receives a push with an AAR in it, the application specified in the AAR * is guaranteed to run. The AAR overrides the tag dispatch system. * You can add it back in to guarantee that this * activity starts when receiving a beamed message. For now, this code * uses the tag dispatch system. *///,NdefRecord.createApplicationRecord("com.example.android.beam") ) } override fun onResume() { super.onResume() // Check to see that the Activity started due to an Android Beam if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) { processIntent(intent) } } override fun onNewIntent(intent: Intent) { // onResume gets called after this to handle the intent setIntent(intent) } /** * Parses the NDEF Message from the intent and prints to the TextView */ private fun processIntent(intent: Intent) { textView = findViewById(R.id.textView) // only one message sent during the beam intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMsgs -> (rawMsgs[0] as NdefMessage).apply { // record 0 contains the MIME type, record 1 is the AAR, if present textView.text = String(records[0].payload) } } } }
Java
package com.example.android.beam; import android.app.Activity; import android.content.Intent; import android.nfc.NdefMessage; import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.nfc.NfcAdapter.CreateNdefMessageCallback; import android.nfc.NfcEvent; import android.os.Bundle; import android.os.Parcelable; import android.widget.TextView; import android.widget.Toast; import java.nio.charset.Charset; public class Beam extends Activity implements CreateNdefMessageCallback { NfcAdapter nfcAdapter; TextView textView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); TextView textView = (TextView) findViewById(R.id.textView); // Check for available NFC Adapter nfcAdapter = NfcAdapter.getDefaultAdapter(this); if (nfcAdapter == null) { Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show(); finish(); return; } // Register callback nfcAdapter.setNdefPushMessageCallback(this, this); } @Override public NdefMessage createNdefMessage(NfcEvent event) { String text = ("Beam me up, Android!\n\n" + "Beam Time: " + System.currentTimeMillis()); NdefMessage msg = new NdefMessage( new NdefRecord[] { createMime( "application/vnd.com.example.android.beam", text.getBytes()) /** * The Android Application Record (AAR) is commented out. When a device * receives a push with an AAR in it, the application specified in the AAR * is guaranteed to run. The AAR overrides the tag dispatch system. * You can add it back in to guarantee that this * activity starts when receiving a beamed message. For now, this code * uses the tag dispatch system. */ //,NdefRecord.createApplicationRecord("com.example.android.beam") }); return msg; } @Override public void onResume() { super.onResume(); // Check to see that the Activity started due to an Android Beam if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) { processIntent(getIntent()); } } @Override public void onNewIntent(Intent intent) { // onResume gets called after this to handle the intent setIntent(intent); } /** * Parses the NDEF Message from the intent and prints to the TextView */ void processIntent(Intent intent) { textView = (TextView) findViewById(R.id.textView); Parcelable[] rawMsgs = intent.getParcelableArrayExtra( NfcAdapter.EXTRA_NDEF_MESSAGES); // only one message sent during the beam NdefMessage msg = (NdefMessage) rawMsgs[0]; // record 0 contains the MIME type, record 1 is the AAR, if present textView.setText(new String(msg.getRecords()[0].getPayload())); } }
AAR に関するコードはコメントアウトされており、削除できます。AAR を有効にすると、AAR で指定されたアプリケーションは常に Android ビーム メッセージを受信します。アプリケーションが存在しない場合、Google Play によりアプリのダウンロードが開始されます。したがって、AAR を使用する場合、Android 4.0 以降のデバイスでは、次のインテント フィルタは技術的には必要ありません。
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="application/vnd.com.example.android.beam"/> </intent-filter>
このインテント フィルタにより、NFC タグをスキャンするか、com.example.android.beam
タイプの AAR を持つ Android ビームを受信したとき、または NDEF 形式のメッセージに application/vnd.com.example.android.beam
タイプの MIME レコードが含まれている場合に、 アプリケーションを起動できるようになります。
AAR でアプリケーションの起動やダウンロードを保証する場合にも、インテント フィルタの使用をおすすめします。AAR で指定されたパッケージ内のメイン アクティビティを常に開始するのではなく、アプリケーションにおいて選択したアクティビティを開始できるためです。AAR にはアクティビティ レベルの粒度はありません。また、一部の Android デバイスでは AAR がサポートされていないため、NDEF メッセージの最初の NDEF レコードに識別情報を埋め込み、AAR が機能しない場合に備えてフィルタしておく必要があります。レコードの作成方法の詳細は、一般的なタイプの NDEF レコードの作成をご覧ください。