NFC Basics
非NDEFdata以及其它更深入技术请參见Advanced NFC。
应用程序能够通过注冊Intent过滤器(Intent Filter)来获取关注的标签信息。
由于NFC表标签的扫描是一个非常短的过程,让用户选择app会使得设备离开标签而断开连接。你应该让你的activity直接处理它关注的标签,而不要弹出activity选择的菜单。
(NFC论坛)
这些技术在Advanced NFC中介绍。我们推荐您使用NDEF标准。由于这样您能够更轻松地开发,而且获得android设备的最大的支持。假设使用其它的格式,您须要自定义协议栈来和标签通信。
当android设备扫描到包括NDEF格式数据的标签的 时候。它会解析出当中的消息,而且获取MIME类别和URI。
系统会读取NdefMessage中的第一个NdefRecord,来决定如何解析整个的NdefMessage(NdefMessage中有多个NedfRecord)。
在一个典型的NDEF格式消息里面。第一个NdefRecord包括以下几个字段:
RTD数值相应的情况请看表2。
一个Ndef message包括非常多的Ndfe Record。所以不要以为所有数据都在第一个record里面。
当MIME类型和URI没法映射为NDEF数据的时候。或者NFC标签不是以NDEF数据开头的。这样的情况下,会产生一个Tag对象,当中包括了标签的技术信息和数据负载,然后Tag对象被封装到了一个ACTION_TECH_DISCOVERED的intent中,而不再产生上面提到的那种Intent。
Type Name Format (TNF) | Mapping |
---|---|
TNF_ABSOLUTE_URI | URI based on the type field. |
TNF_EMPTY | Falls back to ACTION_TECH_DISCOVERED . |
TNF_EXTERNAL_TYPE | URI based on the URN in the type field. The URN is encoded into the NDEF type field in a shortened form: <domain_name>:<service_name> . Android maps this to a URI in the form: vnd.android.nfc://ext/<domain_name>:<service_name> . |
TNF_MIME_MEDIA | MIME type based on the type field. |
TNF_UNCHANGED | Invalid in the first record, so falls back to ACTION_TECH_DISCOVERED . |
TNF_UNKNOWN | Falls back to ACTION_TECH_DISCOVERED . |
TNF_WELL_KNOWN | MIME type or URI depending on the Record Type Definition (RTD), which you set in the type field. See Table 2. for more information on available RTDs and their mappings. |
Table 2. Supported RTDs for TNF_WELL_KNOWN and their mappings
Record Type Definition (RTD) | Mapping |
---|---|
RTD_ALTERNATIVE_CARRIER | Falls back to ACTION_TECH_DISCOVERED . |
RTD_HANDOVER_CARRIER | Falls back to ACTION_TECH_DISCOVERED . |
RTD_HANDOVER_REQUEST | Falls back to ACTION_TECH_DISCOVERED . |
RTD_HANDOVER_SELECT | Falls back to ACTION_TECH_DISCOVERED . |
RTD_SMART_POSTER | URI based on parsing the payload. |
RTD_TEXT | MIME type of text/plain . |
RTD_URI | URI based on payload. |
假设多个应用都过滤(这里是要筛子上面的东西,能够理解为筛选,而不是漏出去的——小马)了这样的intent,就会出现activity选择器供用户选择activity。标签派发系统定义三种类型的标签,以下依照优先级从高到低排序。
<uses-permission android:name="android.permission.NFC" />
API level 10包括了广泛的读写操作的支持,还有前台的NDEF推送,API level14 提供了给其它设备推送NDEF消息的便捷办法,通过Android Beam和其它的创建NDEFrecords的便捷的方法。
<uses-sdk android:minSdkVersion="10"/>
- uses-feature 元素,在应用商店里面指示你的应用仅仅能在NFC设备上面执行。
<uses-feature android:name="android.hardware.nfc" android:required="true" />
假设你的应用仅仅是把nfc作为一个可选功能,那么你能够忽略uses-feature元素,而且在执行时候查看设备是是否支持nfc。
比方通过方法getDefaultAdapter是不是空的。
筛选NFC Intent Filtering for NFC Intents
你的应用在扫描到NFC标签的时候启动。应该在你的应用中加入一种。两种。或者三种所有类型的intent筛选。就在Android Manifest中。然而通常都是倾向于把ACTION_NDEF_DISCOVERED作为启动你应用的选项。
ACTION_TECH_DISCOVERED是第一种没有不论什么应用处理或者负载不是NDEF格式的时候的备选项。
而使用ACTION_TAG_DISCOVERED则会导致筛选太宽泛。
多数的应用都会依照优先级。先使用头两种。这样不会使得你的应用启动的太过频繁。TAG的intent作为最后的选择,在前面两种都没有应答的时候才使用。
以下的部分展示了如何筛选不同类型的intent。
ACTION_NDEF_DISCOVERED
要筛选这个类型的intent,须要声明这个类型。和数据的类型以下这个样例就指定了ACTION_NDEF_DISCOVERED的筛选。和 text/plain的MIME类型。
<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形式的筛选。
http://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
假设你的activity筛选了ACTION_TECCH_DISCOVERED intent,你必须同一时候使用一个 tech-list集来创建一个xml文件,指明你的应用支持哪些的技术类型。假设你的支持的技术集是标签中支持的技术的子集,那么就会匹配。你能够使用getTechList()来获取标签支持的类型。
<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集合。每个集合都作为独立的,你的不论什么的一个list中的技术是getTechList方法返回的标签中包括的技术的子集的时候。就觉得匹配成功。这里面就是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>
</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>
在你的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 intent的很多其它的信息。请參加Working with Supported Tag Technologies,Advance NFC文档。
ACTION_TAG_DISCOVERED
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED"/>
</intent-filter>
从intent中获取信息 Obtaining information from intents
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 操作获得的数组
}
你还能够从intent中获取Tag对象,当中包括了负载数据,而且你能够列举里面的技术类型。
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
创建典型的NDEF记录 Creating Common Types of NDEF Records
这个部分讲述怎样在你写入标签或者AndroidBeam通信的时候怎样创建典型的NDEF记录。从Android 4.0,即API 14開始,你能够使用createUri来自己主动帮你创建URI类型。
从Android4.1,即API 16開始,能够使用creatExternal或者createMine来创建MIME和其它的NDEF类型的record。尽量使用这些便捷的方式来避免手动创建NDEFrecord可能会发生的错误。
TNF_ABSOLUTE_URI
提示:我们推荐您使用更高效的RTD_URI来替代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的筛选器例如以下:
<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 record:
NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam",
"Beam me up, Android".getBytes(Charset.forName("US-ASCII")));
手动创建NdefRecord:
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 record的过滤器如以下这样。
<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
使用高级方法1
NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com");
使用高级方法2
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
使用高级方法:Using the createExternal()
method:
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);
手动创建:Creating the NdefRecord
manually:
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>
在标签部署时使用TNF_EXTERNAL_TYPE来适应更一般的NFC标签,这样能够更好的支持android设备和非android设备。
所以你仅仅须要提供一个域(比方说 样例中的example.com)和一个类型(比方说样例中的 externalType)就能够了。中间用冒号连接。
当派发TNF_EXTERNAL_TYPE类型的时候,Android会将urn:nfc:ext:example.com:externalType的urn转化为vnd.android.nfc://ext/eample.com:externalType
的URI,就是样例中的intent过滤器中定义的那样。
Android应用记录 Android Application Records
AAR在Android4.0中引入,目的是为了在扫描到标签时保证app会被启动。
AAR在NDEFrecord中嵌入了一个应用的包名。
你能够把AAR增加到NDEF message的不论什么一个NDEFrecord其中,由于android会扫描NDEF message中的每个NDEFrecord来寻找AAR。假设找到了就会基于AAR之中的包名来启动app。
假设如今设备里面没有这个app,就执行GooglePlay来開始下载app。
除非AAR是你的NedfMessage中的唯一的record,否则不要把AAR放在NdefMessage的第一个record。这是由于android系统第一个记录来推断MIME类型和标签的URI。
MIME和URI是用来产生给过滤器的intent的。
以下是一个创建AAR的样例:
NdefMessage msg = new NdefMessage(
new NdefRecord[] {
...,
NdefRecord.createApplicationRecord("com.example.android.beam")}
向其它设备发送(Beam)NDEF消息 Beaming NDEF Messages to Other Devices
发起Beam的应用一定要在设备的前台,而接收Beam的设备应该解锁屏幕。当发起设备和接收设备的距离足够近的时候。发起的 设备会出现“点击開始Beam传送的提示”。使用者能够选择是不是Beam给接收设备信息。
很多其它请參见enableForegoundNdefPush方法。
使用Android Beam你还要知道以下几个经验:
假设你的activity也开启了“前台派发”,那么仍然能够捕获在“前台派发”中设置的intent过滤器。
当你的应用要依据当前的上下文来决定推送什么样的消息的时候。就应该使用setNdefPushMessageCallback方法。这样会更加依赖于你的用户在应用中正在做什么。
在activity中onCreate方法中调用,能够在AndroidBeamDemo中查看完整的样例。
以下的样例也能够帮助你创建一个MIME的记录:
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>
AAR没有activity级别的标志。
而且另一些设备不支持AAR,你应该仍然在NDEFmessage的第一个NDEFRecord中嵌入指示信息,而且用过滤器来获取。以防万一。创建record的方法详情请參见Creating common Types fo NDEF records。