参考
利用nfc读取公交卡
nfc:近距离无线通信(Near Field Communication),距离一般要小于4cm。android设备(需要支持nfc技术)和nfc设备(文档中称为tag)进行少量的数据交换。
需要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(); }
当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的匹配顺序如下:
匹配纯文本
<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。
如果没有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>
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>
<intent-filter> <action android:name="android.nfc.action.TAG_DISCOVERED"/> </intent-filter>但通过该action匹配上的可能很少,并且即使匹配上了,在处理nfc设备中的数据时也不方便。所以,不建议配置该filter。
当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应该包含如下信息:
1,3-bit的tnf(Type Name Format):用于表明该如何解析可变长度的type字段。
2,可变长度的type:描述该条record的type。
3,可变长度的id:当前record的id。这个字段用的不多,但如果你需要当前tag的唯一标识,可以通过这个id进行创造。
4,可变长度的负载(payload):程序需要读写的真实数据。由于一个message可以包含多个record,所以全部的负载并不仅仅包含在第一个record中。
@Override 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为:
<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>