首页 > 代码库 > NFC Basics(基本NFC)——翻译自developer.android.com
NFC Basics(基本NFC)——翻译自developer.android.com
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. |
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
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
从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
使用高级方法1NdefRecord 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。
NFC Basics(基本NFC)——翻译自developer.android.com