nfc入门
2015-10-29 17:21
288 查看
基础
参考利用nfc读取公交卡
nfc:近距离无线通信(Near Field Communication),距离一般要小于4cm。android设备(需要支持nfc技术)和nfc设备(文档中称为tag)进行少量的数据交换。
权限及feature配置
需要nfc权限,如下:<uses-permission android:name="android.permission.NFC"/>也可以配置uses-feature。但,如果Nfc并不是app的主要功能,不建议在清单文件中配置。
<uses-feature android:name="android.hardware.nfc" android:required="true" />如果没有配置<uses-feature>可能导致不支持nfc功能的android设备安装上了该应用。可以使用如下代码进行检测判断
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); if (adapter == null) Toast.makeText(MainActivity.this, "nfc不支持", Toast.LENGTH_SHORT).show(); if (adapter != null && !adapter.isEnabled()) { Toast.makeText(MainActivity.this, "nfc未打开", Toast.LENGTH_SHORT).show(); }
action的配置
当android设备扫描到有nfc设备时(未锁屏状态下,android设备会不停地扫描nfc设备,除非用户在手机设置中关闭了nfc功能),系统会发送一个intent。因此,为了获取到该intent, 需要在相应的activity配置中加上<intent-filter>。该intent一共有以下三个action:<action android:name="android.nfc.action.NDEF_DISCOVERED"/> <action android:name="android.nfc.action.TECH_DISCOVERED" /> <action android:name="android.nfc.action.TAG_DISCOVERED" />
三个action的匹配顺序如下:
NDEF_DISCOVERED
当nfc设备(tag)中存储的数据为ndef格式并且系统可以识别数据的类型(MIME Type或者 URI)时,intent的action为该值。如:匹配纯文本
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain" /><!--MIME Type--> </intent-filter>匹配链接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" /><!--uri--> </intent-filter>在上面配置intent-filter时,在<data>标签中指定了数据格式,只有满足该action,并且数据满足格式满足data中指定的这个intent-filter才会通过,才会启动该activity。
TECH_DISCOVERED
如果没有activity处理第一种intent,intent的action便为第二种。如上面的,假如数据格式不匹配,而手机中没有应用能处理action为ndef_discovered时的intent,那么action的intent便会变为tech_discovered。匹配该种Intent时,必须创建一个xml文件(该xml文件放置在res/xml文件夹中)用来指定当前activity所支持的nfc技术,这些技术名称写在tech-list集合中。在清单文件中,通过meta-data将这份xml引入。其中常用的技术及tech-list格式如下:
<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说明
文档原文如下:Your activity is considered a match if a tech-list set is a subset of the technologies that are supported by the tag, which you can obtain by calling getTechList(). You can also specify multiple tech-list sets. Each of the tech-list sets is considered independently, and your activity is considered a match if any single tech-list set is a subset of the technologies that are returned by getTechList().一份xml文件中可以有多个<tech-list>,每一个<tech-list>可以指定多个<tech>标签。假设设备支持的tech为集合A,只要有一个<tech-list>为集合A的子集,那么第二个intent便会匹配成功。否则不会。
如北京一卡通的tech为:android.nfc.tech.IsoDep与android.nfc.tech.NfcA。那么xml如下的话:
<tech-list> <tech>android.nfc.tech.IsoDep</tech> </tech-list> <tech-list> <tech>android.nfc.tech.NfcV</tech> </tech-list> <tech-list> <tech>android.nfc.tech.NfcF</tech> </tech-list>该intent便会匹配成功。将第一个<tech-list>换成
<tech-list> <tech>android.nfc.tech.IsoDep</tech> <tech>android.nfc.tech.NfcA</tech> </tech-list>也会匹配成功。但如果将第一个换成:
<tech-list> <tech>android.nfc.tech.IsoDep</tech> <tech>android.nfc.tech.NfcB</tech> </tech-list>那么第二个intent便不会匹配成功,因为NfcB并不在设备支持的集合中,所以该xml文件中没有一个<tech-list>是一卡通支持的子集。
示例
其中的nfc_tech_filter便是上面的<tech-list>所在的xml文件。<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>
TAG_DISCOVERED
如果经过前两种仍没有找到activity匹配上,那么intent的action便会降为tag_discovered。配置如下:<intent-filter> <action android:name="android.nfc.action.TAG_DISCOVERED"/> </intent-filter>但通过该action匹配上的可能很少,并且即使匹配上了,在处理nfc设备中的数据时也不方便。所以,不建议配置该filter。
获取数据
当该activity是因为nfc的intent被启动时, 可以从intent中获取这个设备的信息。intent含有如下的extra信息:1,EXTRA_TAG(必需的):一个代表着当前设备的Tag对象。
2,EXTRA_NDEF_MESSAGES(可选的):从设备中解析的一系列的NDEF message。当intent的action为ACTION_NDEF_DISCOVERED,该extra便是必需的,一定有的。
3,EXTRA_ID(可选的):设备的id(low_level id)。
ndef格式
当action为ndef_discovered时,系统会自动将nfc设备所存储的信息封装成NdefMessage对象,而一个NdefMessage中含有若干个NdefRecord对象。获取代码如下:if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {//判断是不是ndef_discovered NdefMessage[] extra = (NdefMessage[]) getIntent().getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); NdefRecord[] records = extra[0].getRecords(); Log.e(TAG, "读取到有数据:"+new String(records[0].getPayload()));虽然说获取到的NdefMessage数组大部分时候只有一个元素,但为了兼容以后的发展,故这里将NdefMessage数组封装到intent中了。
由于一个message在可能含有多个record对象,所以nfc设备中的数据信息并不单单存储到第一个record中。
其余
分两步:第一步,获取tag对象:
Tag extra = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);第二步:根据nfc设备支持的技术,将tag转换成相应的对象。如一卡通支持android.nfc.tech.IsoDep,转换如下:
IsoDep dep = IsoDep.get(extra);
这些类都在android.nfc.tech包中。而且所有的都是通过静态方法get()获取相应的对象。
NdefRecord
NdefRecord应该包含如下信息:1,3-bit的tnf(Type Name Format):用于表明该如何解析可变长度的type字段。
2,可变长度的type:描述该条record的type。
3,可变长度的id:当前record的id。这个字段用的不多,但如果你需要当前tag的唯一标识,可以通过这个id进行创造。
4,可变长度的负载(payload):程序需要读写的真实数据。由于一个message可以包含多个record,所以全部的负载并不仅仅包含在第一个record中。
示例
@Override其清单文件intent-filter为:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this); if (adapter == null) Toast.makeText(MainActivity.this, "nfc不支持", Toast.LENGTH_SHORT).show(); if (adapter != null && !adapter.isEnabled()) { Toast.makeText(MainActivity.this, "nfc未打开", Toast.LENGTH_SHORT).show(); }
onNewIntent(getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
NdefMessage[] extra = (NdefMessage[]) intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefRecord[] records = extra[0].getRecords();
Log.e(TAG, "读取到有数据:" + new String(records[0].getPayload()));
} else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
Tag extra = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//android.nfc.tech.IsoDep android.nfc.tech.NfcA
String[] list = extra.getTechList();
for (String tech : list) {
Log.e(TAG, tech);//输出当前nfc设备支持的tech
}
final IsoDep dep = IsoDep.get(extra);
if (dep != null) {
Toast.makeText(MainActivity.this, "iso dep", Toast.LENGTH_SHORT).show();
}
}
}
<intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> <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" />其中的nfc_tech_filter如下:
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list> <tech>android.nfc.tech.IsoDep</tech> </tech-list> <tech-list> <tech>android.nfc.tech.NfcV</tech> </tech-list> <tech-list> <tech>android.nfc.tech.NfcF</tech> </tech-list>
</resources>
相关文章推荐
- java lucene搜索功能初探(1)
- 升级xcode7的问题:使用shareSDK,坑的你两眼泪汪汪
- java设计模式之代理模式
- 1014 C语言文法定义与C程序的推导过程
- C语言文法阅读与理解序
- js中运算符的优先级
- -Android Studio导入Project的方法
- Python 基础——排列组合的实现
- 队列 数据结构作业(二)
- Effective C++笔记(二)
- C语言文法定义
- NSNumber 用法
- 整理看过的书籍,与大家分享
- I - Jurassic Remains
- Material Design - Curved motion
- C++ 中的回调函数
- iOS APP中数据加载的6种方式
- 性能分析之-- JAVA Thread Dump 分析综述
- 探索iOS 9适配
- 压力测试相关之ab命令