要开发NFC,首先确定设备需要支持NFC功能
申请权限
在清单文件中声明所需要的权限,该权限并不需要动态申请
1 2 3 4
| <uses-permission android:name="android.permission.NFC"/>
<uses-feature android:name="android.hardware.nfc" android:required="true"/>
|
配置NFC
给对应的Activity添加NFC配置,该配置有三种类型:NDEF、TECH、TAG,从左到右过滤优先级依次降低,对应的Intent过滤规则如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <activity android:name=".activity.TeacherLogin" android:exported="false" android:screenOrientation="landscape"> <intent-filter> <action android:name="android.nfc.action.NDEF_DISCOVERED"/> </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" /> <intent-filter> <action android:name="android.nfc.action.TAG_DISCOVERED" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
|
注意activity的meta-data中引用了一个xml配置文件,这个配置文件用于指定程序支持的nfc卡的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <resources> <tech-list> <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.IsoDep</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>
|
不同NFC使用场景
NFC数据格式名称 | ISO标准名称 | 实际应用场合 |
---|
NfcA | ISO 14443-3A | 门禁卡 |
NfcB | ISO 14443-3B | 二代身份证 |
NfcF | JIS 6319-4 | 香港八达通 |
NfcV | ISO 15693 | 深圳图书馆读者证 |
IsoDep | ISO 14443-4 | 北京一卡通、深圳通、西安长安通、武汉通、广州羊城通 |
NFC读取数据
初始化NFC适配器
首先声明一个NFC适配器,和一个延迟意图
1 2
| private NfcAdapter nfcadapter; private PendingIntent pendingIntent;
|
获取到nfc适配器实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| private void initNFC() { nfcAdapter = NfcAdapter.getDefaultAdapter(this); if (nfcAdapter == null){ ToastUtils.show("设备不支持NFC", this); } else if (!nfcAdapter.isEnabled()){ ToastUtils.show("请先打开NFC功能", this); } else { ToastUtils.show("NFC功能正常", this);
Intent intent = new Intent(this, getClass()); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } }
|
检测NFC读取
在页面的onResume中读取数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| @Override protected void onResume(){ super.onResume();
if (nfcAdapter == null || !nfcAdapter.isEnabled()){ return; }
IntentFilter filter1 = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED); IntentFilter filter2 = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED); IntentFilter filter3 = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED); try { filter1.addDataType("*/*"); } catch (IntentFilter.MalformedMimeTypeException e) { e.printStackTrace(); }
IntentFilter[] intentFilters = {filter1, filter2, filter3};
String[][] techList = new String[][]{ new String[]{NfcA.class.getName()}, new String[]{IsoDep.class.getName()} };
nfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilters, techList); }
|
读取NFC数据
onResume()收到被调用后,将会调用newIntent()读取NFC收到的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); String action = intent.getAction(); Log.d(TAG, "onNewIntent: action = " + action);
if (action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED) || action.equals(NfcAdapter.ACTION_TECH_DISCOVERED) || action.equals(NfcAdapter.ACTION_TAG_DISCOVERED) ) { Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); byte[] tagId = tag.getId(); String hexString = byteArrayToHexString(tagId); Log.d(TAG, "onNewIntent: 读取数据 = " + hexString); } }
public static String byteArrayToHexString(byte[] bytes) { StringBuilder stringBuilder = new StringBuilder(); if (bytes == null || bytes.length <= 0) { return null; } for (byte b : bytes) { String hv = Integer.toHexString(b & 0xFF); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); }
|
如此,整个过程完成,hexString就是NFC读取的数据了
禁止读取NFC数据
由于当前页面接管了NFC数据读取,当应用进入后台后,我希望它不再读取数据,所以在onPause()方法中添加禁止读取
1 2 3 4 5 6 7 8
| @Override public void onPause(){ super.onPause(); if (nfcAdapter == null || !nfcAdapter.isEnabled()){ return; } nfcAdapter.disableForegroundDispatch(this); }
|
其他扩展
关于uses-feature
用于指定Android是否需要某个硬件功能或者软件功能的支持
格式
1 2 3 4
| <uses-feature android:name="string" android:required=["true" | "false"] android:glEsVersion="integer" />
|
释义
属性 | 释义 |
---|
android:name | 以描述符字符串的形式指定应用使用的单个硬件或软件功能,硬件功能和软件功能部分已列出有效的属性值参考下方uses-feature 的name |
android:required | 表示应用是否需要 android:name 中所指定功能,为true时即是规定当设备不具有该指定功能时,应用无法正常工作,或设计为无法正常工作;为false则意味着如果设备具有该功能,应用会在必要时优先使用该功能,但应用设计为不使用该指定功能也可正常工作 |
android:glEsVersion | 应用需要的 OpenGL ES 版本。高 16 位表示主版本号,低 16 位表示次版本号 |
uses-feature
的name
name | 释义 |
---|
android.hardware.audio.low_latency | 应用使用设备的低延迟时间音频管道,该管道可以减少处理声音输入或输出时的滞后和延迟。 |
android.hardware.audio.output | 应用使用设备的扬声器、音频耳机插孔、蓝牙流式传输能力或类似机制传输声音。 |
android.hardware.audio.pro | 应用使用设备的高端音频功能和性能能力。 |
android.hardware.microphone | 应用使用设备的麦克风记录音频。 |
android.hardware.bluetooth | 应用使用设备的蓝牙功能,通常用于与其他支持蓝牙的设备进行通信。 |
android.hardware.bluetooth_le | 应用使用设备的低功耗蓝牙无线电功能。 |
更多参考: https://developer.android.google.cn/guide/topics/manifest/uses-feature-element