当前位置:   article > 正文

Android硬件通信之 NFC通信_android nfc

android nfc

一,简介

1.1 NFC是继wifi和蓝牙之后,又一种新的近距离无线传输技术,这种局限性比较大,设备必须装有NFC硬件才能实现数据传输,而且传输距离非常短,4cm之内才能传输,真实场景可能触碰才能传输。

1.2 NFC优缺点

优点:传输速度快;连接方便,触碰就能实现通信;安全性高

缺点:传输距离非常短;传输数据量小;并不是全部普及,部分手机不支持

1.3 NFC,蓝牙,红外对比

对比项NFC蓝牙红外
网络类型点对点单点对多点点对点
有效距离<=0.1m<=10m,最新的蓝牙4.0有效距离可达100m一般在1m以内,热技术连接,不稳定
传输速度最大424kbps最大24Mbps慢速115.2kbp5,快速4Mbps
连接时间<0.1s6s0.5s
安全性安全,硬件实现安全,软件实现个安全,使用IRFM时除外
通信模式主动-主动/被动主动-主动主动-主动
成本

1.4 NFC通信模式

读卡器模式(Reader/writer mode)、仿真卡模式(Card Emulation Mode)、点对点模式(P2P mode)。

读卡器模式

数据在NFC芯片中,可以简单理解成“刷标签”。本质上就是通过支持NFC的手机或其它电子设备从带有NFC芯片的标签、贴纸、名片等媒介中读写信息。通常NFC标签是不需要外部供电的。当支持NFC的外设向NFC读写数据时,它会发送某种磁场,而这个磁场会自动的向NFC标签供电。

仿真卡模式

数据在支持NFC的手机或其它电子设备中,可以简单理解成“刷手机”。本质上就是将支持NFC的手机或其它电子设备当成借记卡、公交卡、门禁卡等IC卡使用。基本原理是将相应IC卡中的信息凭证封装成数据包存储在支持NFC的外设中 。

在使用时还需要一个NFC射频器(相当于刷卡器)。将手机靠近NFC射频器,手机就会接收到NFC射频器发过来的信号,在通过一系列复杂的验证后,将IC卡的相应信息传入NFC射频器,最后这些IC卡数据会传入NFC射频器连接的电脑,并进行相应的处理(如电子转帐、开门等操作)。

点对点模式

该模式与蓝牙、红外差不多,用于不同NFC设备之间进行数据交换,不过这个模式已经没有有“刷”的感觉了。其有效距离一般不能超过4厘米,但传输建立速度要比红外和蓝牙技术快很多,传输速度比红外块得多,如过双方都使用Android4.2,NFC会直接利用蓝牙传输。这种技术被称为Android Beam。所以使用Android Beam传输数据的两部设备不再限于4厘米之内。

点对点模式的典型应用是两部支持NFC的手机或平板电脑实现数据的点对点传输,例如,交换图片或同步设备联系人。因此,通过NFC,多个设备如数字相机,计算机,手机之间,都可以快速连接,并交换资料或者服务。

二,NFC通信步骤

2.1 AndroidManifest.xml添加NFC使用权限,不用动态请求

  1. <!--NFC 相关权限-->
  2. <!--描述所需硬件特性-->
  3. <uses-feature
  4. android:name="android.hardware.nfc"
  5. android:required="true" />
  6. <uses-permission android:name="android.permission.NFC" />

2.2 添加NFC标签过滤,两种方式

方式一:AndroidManifest.xml的Activity标签添加xml配置

res目录下 新建xml文件夹,再新建nfc_tech_filter.xml文件

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
  3. <!-- 可以处理所有Android支持的NFC类型 -->
  4. <tech-list>
  5. <tech>android.nfc.tech.IsoDep</tech>
  6. </tech-list>
  7. <tech-list>
  8. <tech>android.nfc.tech.NfcA</tech>
  9. </tech-list>
  10. <tech-list>
  11. <tech>android.nfc.tech.NfcB</tech>
  12. </tech-list>
  13. <tech-list>
  14. <tech>android.nfc.tech.NfcF</tech>
  15. </tech-list>
  16. <tech-list>
  17. <tech>android.nfc.tech.NfcV</tech>
  18. </tech-list>
  19. <tech-list>
  20. <tech>android.nfc.tech.Ndef</tech>
  21. </tech-list>
  22. <tech-list>
  23. <tech>android.nfc.tech.NdefFormatable</tech>
  24. </tech-list>
  25. <tech-list>
  26. <tech>android.nfc.tech.MifareUltralight</tech>
  27. </tech-list>
  28. <tech-list>
  29. <tech>android.nfc.tech.MifareClassic</tech>
  30. </tech-list>
  31. </resources>

activity标签配置该xml

  1. <activity
  2. android:name=".MainActivity"
  3. android:exported="true">
  4. <intent-filter>
  5. <action android:name="android.intent.action.MAIN" />
  6. <category android:name="android.intent.category.LAUNCHER" />
  7. </intent-filter>
  8. <intent-filter>
  9. <action android:name="android.nfc.action.TECH_DISCOVERED" />
  10. </intent-filter>
  11. <meta-data
  12. android:name="android.nfc.action.TECH_DISCOVERED"
  13. android:resource="@xml/nfc_tech_filter" />
  14. </activity>

 方式二:初始化NFC时候动态配置标签

  1. /**
  2. * 初始化nfc设置
  3. */
  4. public static void NfcInit(Activity activity) {
  5. Intent intent = new Intent(activity, activity.getClass());
  6. intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
  7. mPendingIntent = PendingIntent.getActivity(activity, 0, intent, 0);
  8. //做一个IntentFilter过滤你想要的action 这里过滤的是ndef
  9. IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
  10. try {
  11. filter.addDataType("*/*");
  12. } catch (IntentFilter.MalformedMimeTypeException e) {
  13. e.printStackTrace();
  14. }
  15. mTechList = new String[][]{{MifareClassic.class.getName()},
  16. {NfcA.class.getName()}};
  17. //生成intentFilter
  18. mIntentFilter = new IntentFilter[]{filter};
  19. }

2.3 创建NFC适配器

NFC所有的操作都是通过NfcAdapter完成的,创建NfcAdapter后,同时新建一个等待的PendingIntent,这个主要用于出发NFC打开该页面

  1. private NfcAdapter mNfcAdapter;
  2. private PendingIntent mPendingIntent;
  3. @Override
  4. protected void onStart() {
  5. super.onStart();
  6. mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
  7. mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass())
  8. .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
  9. }

2.4 在onResume中开启前台调度,来初始化NFC

  1. //在onResume中开启前台调度
  2. @Override
  3. protected void onResume() {
  4. super.onResume();
  5. //设定intentfilter和tech-list。如果两个都为null就代表优先接收任何形式的TAG action。也就是说系统会主动发TAG intent。
  6. if (mNfcAdapter != null) {
  7. mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null); //启动 }
  8. }
  9. }

2.5 在onNewIntent中处理由NFC设备传递过来的intent

  1. //在onNewIntent中处理由NFC设备传递过来的intent
  2. @Override
  3. protected void onNewIntent(Intent intent) {
  4. super.onNewIntent(intent);
  5. processIntent(intent);
  6. }

2.6 处理读取过来的数据,包括卡ID(通常为设备序列号,16进制形式09:D1:E6:6B)

然后NFC卡其它信息不一定有,NFC卡标签这些信息可能是没有的,但序列号是一定有的,即NFC卡的唯一标识

  1. //这块的processIntent() 就是处理卡中数据的方法
  2. public void processIntent(Intent intent) {
  3. try {
  4. tvContent.setText("");
  5. // 检测卡的id
  6. String id = readNFCId(intent);
  7. // NfcUtils中获取卡中数据的方法
  8. String result = readNFCFromTag(intent);
  9. StringBuilder stringBuilder = new StringBuilder();
  10. stringBuilder.append("卡ID十六进制:" + id).append("\r\n");
  11. stringBuilder.append("卡ID十进制:" + hexToDec(id)).append("\r\n");
  12. stringBuilder.append("信息:").append("\r\n");
  13. stringBuilder.append(result).append("\r\n");
  14. tvContent.setText(stringBuilder);
  15. // 往卡中写数据
  16. //String data = "this.is.write";
  17. //writeNFCToTag(data, intent);
  18. } catch (Exception e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. /**
  23. * 读取nfcID
  24. */
  25. public String readNFCId(Intent intent) throws UnsupportedEncodingException {
  26. Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
  27. String id = ByteArrayToHexString(tag.getId());
  28. return id;
  29. }
  30. /**
  31. * 读取NFC的数据
  32. */
  33. public String readNFCFromTag(Intent intent) throws UnsupportedEncodingException {
  34. Parcelable[] rawArray = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
  35. StringBuilder stringBuilder = new StringBuilder();
  36. if (rawArray != null) {
  37. for (int i = 0; i < rawArray.length; i++) {
  38. NdefMessage mNdefMsg = (NdefMessage) rawArray[i];
  39. for (int j = 0; j < mNdefMsg.getRecords().length; i++) {
  40. NdefRecord mNdefRecord = mNdefMsg.getRecords()[j];
  41. if (mNdefRecord != null) {
  42. String readResult = new String(mNdefRecord.getPayload(), "UTF-8");
  43. stringBuilder.append(readResult).append("\r\n");
  44. }
  45. }
  46. }
  47. }
  48. return stringBuilder.toString();
  49. }
  50. /**
  51. * 十六进制转10进制
  52. * @param s
  53. * @return
  54. */
  55. public static int hexToDec(String s) {
  56. String s1 = s.toUpperCase(); // 全转大写
  57. char[] chars = s1.toCharArray(); // 转成 char 数组
  58. Stack<Character> stack = new Stack<>();
  59. for (int i = 0; i < chars.length; i++) {
  60. stack.push(chars[i]); // 放入栈中,倒序遍历
  61. }
  62. int sum = 0; // 定义总和
  63. int size = stack.size(); // 要先赋值给 size ,不然 stack.pop() 之后 size 会变
  64. for (int i = 0; i < size; i++) {
  65. Character pop = stack.pop();
  66. if (String.valueOf(pop).matches("[A-F]")) { // 如果是 A-F
  67. sum += (Math.pow(16, i) * ((pop - 55))); // A的ASCII码为 65,取偏移量
  68. } else { // 如果是纯数字
  69. sum += Math.pow(16, i) * Integer.parseInt(String.valueOf(pop));
  70. }
  71. }
  72. return sum;
  73. }
  74. /**
  75. * 将字节数组转换为字符串
  76. */
  77. private String ByteArrayToHexString(byte[] inarray) {
  78. int i, j, in;
  79. String[] hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
  80. String out = "";
  81. for (j = 0; j < inarray.length; ++j) {
  82. in = (int) inarray[j] & 0xff;
  83. i = (in >> 4) & 0x0f;
  84. out += hex[i];
  85. i = in & 0x0f;
  86. out += hex[i];
  87. }
  88. return out;
  89. }

2.7 往NFC写入数据

  1. /**
  2. * 往nfc写入数据
  3. */
  4. public static void writeNFCToTag(String data, Intent intent){
  5. try {
  6. Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
  7. Ndef ndef = Ndef.get(tag);
  8. ndef.connect();
  9. NdefRecord ndefRecord = null;
  10. if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
  11. ndefRecord = NdefRecord.createTextRecord(null, data);
  12. }
  13. NdefRecord[] records = {ndefRecord};
  14. NdefMessage ndefMessage = new NdefMessage(records);
  15. ndef.writeNdefMessage(ndefMessage);
  16. }catch (Exception e){
  17. }
  18. }
  1. // 往卡中写数据
  2. String data = "this.is.write";
  3. writeNFCToTag(data, intent);

 2.8 取消NfcAdapter调度

  1. @Override
  2. protected void onPause() {
  3. super.onPause();
  4. if (mNfcAdapter != null) {
  5. mNfcAdapter.disableForegroundDispatch(this);
  6. }
  7. }

2.9 销毁NfcAdapter

  1. @Override
  2. protected void onDestroy() {
  3. super.onDestroy();
  4. mNfcAdapter = null;
  5. }

2.10 总结

NFC适合卡片类刷卡数据传输场景,比如门禁卡,支付卡,地铁卡等,快捷方便

三 NFC读卡器通信

3.1  一些手机是不支持NFC的,那么可以选择备选方案,读卡器usb接口插入手机使用。

3.2 但读卡器种类也有很多种,有些是支持键盘模式的,有些是支持usb通信模式的,还有些是支持串口,wifi通信模式。下面主要说下键盘模式,和usb模式通信

3.3 键盘模式比较简单,简单键盘输入事件就可以,大部分扫码枪,读卡器是支持这种模式的,蛀牙源码

  1. private StringBuffer KeyStringBuffer;
  2. @Override
  3. public boolean dispatchKeyEvent(KeyEvent event) {
  4. if (event.getAction() == KeyEvent.ACTION_DOWN) {
  5. int unicodeChar = event.getUnicodeChar();
  6. String number = String.valueOf((char) unicodeChar);
  7. if (KeyStringBuffer == null) {
  8. KeyStringBuffer = new StringBuffer();
  9. }
  10. KeyStringBuffer.append(number);
  11. if (KeyStringBuffer != null && KeyStringBuffer.toString().length() >= 8) {
  12. //都取到的数据
  13. String resultString = KeyStringBuffer.toString();
  14. KeyStringBuffer = null;
  15. Log.e("EEE", "dispatchKeyEvent: " + resultString);
  16. .........自己业务
  17. }
  18. }
  19. return super.dispatchKeyEvent(event);
  20. }

3.4 USB模式,通过转接器连接手机,usb连接后可能通过读卡器内部协议来进行usb通信,所以需要读卡器厂的协议指令。下面是通用usb通信:

  1. public class MainActivity extends AppCompatActivity {
  2. private static final String TAG = "MainActivity";
  3. private static final String ACTION_USB_PERMISSION =
  4. "com.android.example.USB_PERMISSION";
  5. private UsbManager manager;
  6. private UsbDevice device;
  7. @Override
  8. protected void onCreate(Bundle savedInstanceState) {
  9. super.onCreate(savedInstanceState);
  10. setContentView(R.layout.activity_main);
  11. }
  12. public void openUsbClick(View view) {
  13. initUsbManager();
  14. }
  15. /**
  16. * 初始化UsbManager
  17. */
  18. private void initUsbManager() {
  19. //获取UsbManager
  20. manager = (UsbManager) getSystemService(Context.USB_SERVICE);
  21. //查找设备
  22. HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
  23. Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
  24. while (deviceIterator.hasNext()) {
  25. UsbDevice device = deviceIterator.next();
  26. Log.e(TAG, "onCreate: " + device.getDeviceName());
  27. requestPermission(device);
  28. }
  29. }
  30. /**
  31. * 请求权限
  32. */
  33. private void requestPermission(UsbDevice device) {
  34. //请求权限
  35. PendingIntent permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
  36. IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
  37. registerReceiver(usbReceiver, filter);
  38. manager.requestPermission(device, permissionIntent);
  39. }
  40. /**
  41. * 权限回调
  42. */
  43. private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
  44. public void onReceive(Context context, Intent intent) {
  45. String action = intent.getAction();
  46. if (ACTION_USB_PERMISSION.equals(action)) {
  47. synchronized (this) {
  48. device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
  49. if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
  50. if (device != null) {
  51. //call method to set up device communication
  52. openDevice();
  53. }
  54. } else {
  55. Log.d(TAG, "permission denied for device " + device);
  56. }
  57. }
  58. }
  59. }
  60. };
  61. /**
  62. * 打开设备,接口数据
  63. */
  64. private static int TIMEOUT = 100;
  65. private boolean forceClaim = true;
  66. private UsbDeviceConnection connection;
  67. private UsbInterface usbInterface;
  68. private UsbEndpoint usbEndpoint;
  69. private void openDevice() {
  70. if (connection == null) {
  71. //打开设备
  72. connection = manager.openDevice(device);
  73. // 配置设备接口
  74. usbInterface = device.getInterface(0);
  75. connection.claimInterface(usbInterface, forceClaim);
  76. // 获取设备端点
  77. usbEndpoint= usbInterface.getEndpoint(0);
  78. readData();
  79. }
  80. //发送数据
  81. //byte[] dataToSend = "aa".getBytes(); // 需要发送的数据
  82. //int bytesSent = connection.bulkTransfer(usbEndpoint, dataToSend, dataToSend.length, TIMEOUT);
  83. }
  84. //读取数据
  85. boolean isReading = true;
  86. private void readData(){
  87. new Thread(new Runnable(){
  88. @Override
  89. public void run() {
  90. while (isReading){
  91. // 接收数据
  92. byte[] buffer = new byte[1024];
  93. int byteCount = connection.bulkTransfer(usbEndpoint, buffer, buffer.length, 1000);
  94. if (byteCount > 0) {
  95. // 读取到有效数据
  96. String data = new String(buffer, 0, byteCount);
  97. // 处理数据
  98. Log.e(TAG, "data: "+data );
  99. }
  100. }
  101. }
  102. }).start();
  103. }
  104. /**
  105. * 关闭设备
  106. */
  107. public void closeConnect() {
  108. if (connection != null) {
  109. connection.releaseInterface(usbInterface);
  110. connection.close();
  111. }
  112. }
  113. @Override
  114. protected void onDestroy() {
  115. super.onDestroy();
  116. closeConnect();
  117. }
  118. }
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/314778
推荐阅读
相关标签
  

闽ICP备14008679号