此文爲我一時興起花費將近兩天翻譯,肯定問題多多,慢慢完善,敬請諒解。
官網原文鏈接:https://developer.android.com/guide/topics/connectivity/nfc/nfc.html#creating-records
NFC 基礎知識(NFC Basics)
- 從NFC tag裏讀取數據
- 通過Android Beam將NDEF消息從一臺設備發送到另一臺設備
標籤發佈系統(The Tag Dispatch System)
- 解析NFC tag並找出MIME類型或者標識一個tag有效負載的URI。
- 將MIME類型或者URI以及有效負載封裝到一個intent。前兩個步驟在How NFC tags are mapped to MIME types and URIs裏描述了。
- 通過intent啓動一個activity。這個在 How NFC Tags are Dispatched to Applications有說明。
NFC tag如何映射MIME 類型和URI(How NFC tags are mapped to MIME types and URIs)
記錄:要下載完整的NDEF規範請到NFC Forum Specification Download網址,通過查閱Creating common types of NDEF records得知如何構建NDEF記錄。
3字節的TNF(格式化類型名字)(3-bit TNF (Type Name Format))
可變長度類型(Variable length type)
可變長度的ID(Variable length ID)
可變長度的有效負載(Variable length payload)
NFC 標籤如何被分派到應用程序(How NFC Tags are Dispatched to Applications)
在Manifest文件中聲明NFC權限(Requesting NFC Access in the Android Manifest)
- NFC用戶權限 以用來訪問NFC硬件:
<uses-permission android:name="android.permission.NFC" />
- 你的應用程序支持的最小SDK版本。API 9僅僅支持ACTION_TAG_DISCOVERED,而且只允許ERTRA_NDEF_MESSAGE類型的NDEF消息。沒有其他標籤屬性或者I/O操作可以做到。API 10支持讀/寫以及前臺NDEF推送,而到API 14則提供了一種更簡單的 即通過Android Beam來推送NDEF消息,還有其他一些便利的創建NDEF記錄的方法。
<uses-sdk android:minSdkVersion="10"/>
- uses-feature元素能讓你的應用在谷歌商店中顯示在有NFC硬件的設備上:
<uses-feature android:name="android.hardware.nfc" android:required="true" />
NFC intent過濾(Filtering for NFC Intents)
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>
http://developer.android.com/index.html】的URI的filter示例:
<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
<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>
<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>
</resources>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.NfcB</tech>
<tech>android.nfc.tech.Ndef</tech>
</tech-list>
</resources>
<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_TAG_DISCOVERED
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED"/>
</intent-filter>
得到intent中的信息(Obtaining information from intents)
- EXTRA_TAG(必需的):一個表示掃描到的tag的Tag對象。
- EXTRA_NDEF_MESSAGE(可選的):一個從tag中解析到的NDEF信息的數組。這個extra強制託管在ACTION_NDEF_DISCOVERED類型的intent中。
- EXTRA_ID(可選的):標籤的低級別 ID。
public void onResume() {
super.onResume();
...
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
}
}
}
//process the msgs array
}
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
創建常見類型的NDEF記錄(Creating Common Types of NDEF Records)
TNF_ABSOLUTE_URI
NdefRecord uriRecord = new NdefRecord(
NdefRecord.TNF_ABSOLUTE_URI ,
"http://developer.android.com/index.html".getBytes(Charset.forName("US-ASCII")),
new byte[0], new byte[0]);
<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
NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam",
"Beam me up, Android".getBytes(Charset.forName("US-ASCII")));
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")));
<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 with RTD_TEXT
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;
}
<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 with RTD_URI
NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com");
Uri uri = new Uri("http://example.com");
NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);
byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII"));
byte[] payload = new byte[uriField.length + 1]; //add 1 for the URI Prefix
byte 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);
<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
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);
byte[] payload;
...
NdefRecord extRecord = new NdefRecord(
NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType", new byte[0], payload);
<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>
urn:nfc:ext:example.com:externalType,然而NFC論壇的RTD規範聲明URN的一部分【
urn:nfc:ext:】必須在NDEF記錄中被省略。所以你需要提供的是被冒號分隔的域名(example.com
in
the example)和類型(externalType
in
the example)。當分發TNF_EXTERNAL_TYPE的時候,Android將【urn:nfc:ext:example.com:externalType】URN轉換爲【vnd.android.nfc://ext/example.com:externalType】的URL,這些在例子中的intent-filter有聲明。Android應用程序記錄(Android Application Records)
NdefMessage msg = new NdefMessage(
new NdefRecord[] {
...,
NdefRecord.createApplicationRecord("com.example.android.beam")}
發送NDEF消息到其他設備(Beaming NDEF Messages to Other Devices)
- setNdefPushMessage():( Accepts an NdefMessage to set as the message to beam)。當兩設備足夠靠近的時候自動發送消息。
- setNdefPushMessageCallback():接收一個包含createNdefMessage() 並當消息接收設備在範圍內就會調用 該方法的回調。這回調讓你在需要的時候才創建NDEF消息。
- 發送數據的activity必須處於前臺。所有設備都必須處於解鎖狀態。
- 你必須將你要發送的信息封裝到 NdefMessage 對象裏。
- 數據接收設備必須支持 com.android.npp 的NDEF推送協議或者NFC論壇的SNEP(Simple NDEF Exchange Protocol:簡單NDEF交換協議)。com.android.npp 協議要求設備在API 9(Android 2.3)到API 13(Android 3.2)。com.android.npp 和 SNDP都要求API 14(Android 4.0)或以上。
- 創建一個包含你想推送到其他設備的 NdefRecord 的 NdefMessage 。
- 通過一個NdefMessage調用 setNdefPushMessage(),或者在你的 activity的onCteate()方法中通過一個NfcAdapter.CreateNdefMessageCallback的對象調用 setNdefPushMessageCallback。這些方法要求至少一個activity(that you want to enable with Android Beam),伴隨着被激活的其它activity的可選列表。
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 mNfcAdapter;
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
mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (mNfcAdapter == null) {
Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show();
finish();
return;
}
// Register callback
mNfcAdapter.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()));
}
}
<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>
com.example.android.beam
這個應用能夠在掃描到NFC
tag或者接收到com.example.android.beam這種AAR的Android Beam發送的消息,或者當一個NDEF格式的消息包含MIME類型爲application/vnd.com.example.android.beam
的記錄。