Android NFC开发-实践篇

在Android NFC开发-理论篇中,我们了解了在Android中开发NFC的一些理论知识,这篇我们继续应用我们上一篇学到的知识,实现对NDEF格式标签和MifareClassic格式标签的读写操作。

基本操作

配置AndroidMenifest.xml:

<!--API level 9只包含有限的tag支持,包括:.通过ACTION_TAG_DISCOVERED来发布Tag信息.只有通过EXTRA_NDEF_MESSAGES扩展来访问NDEF消息.其他的tag属性和I/O操作都不支持所以你可能想要用API level 10来实现对tag的广泛的读写支持。--><uses-sdk android:minSdkVersion="10"/><!--NFC权限--><uses-permission android:name="android.permission.NFC" /><!-- 要求当前设备必须要有NFC芯片 --><uses-feature android:name="android.hardware.nfc" android:required="true" />

获取设备默认的NfcAdapter对象,判断该设备是否支持NFC功能,若支持,判断此功能是否打开,并且创建一个PendingIntent对象,用于当NFC标签被检测到时,启动我们处理NFC标签的Activity

@Overrideprotected void onStart() {super.onStart();mNfcAdapter= NfcAdapter.getDefaultAdapter(this);//设备的NfcAdapter对象if(mNfcAdapter==null){//判断设备是否支持NFC功能Toast.makeText(this,"设备不支持NFC功能!",Toast.LENGTH_SHORT);finish();return;}if (!mNfcAdapter.isEnabled()){//判断设备NFC功能是否打开Toast.makeText(this,"请到系统设置中打开NFC功能!",Toast.LENGTH_SHORT);finish();return;}mPendingIntent=PendingIntent.getActivity(this,0,new Intent(this,getClass()),0);//创建PendingIntent对象,当检测到一个Tag标签就会执行此Intent}

在OnNewIntent()方法中,获取到Tag对象

@Overrideprotected void onNewIntent(Intent intent) {super.onNewIntent(intent);mTag=intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);//获取到Tag标签对象String[] techList=mTag.getTechList();System.out.println("标签支持的tachnology类型:");for (String tech:techList){System.out.println(tech);}}

为了更好的处理NFC标签,我们需要在Activity获取焦点时(onResume),在主线程中启动前台发布系统,并且在Activity失去焦点时,关闭前台发布系统

//页面获取焦点
@Overrideprotected void onResume() {super.onResume();if (mNfcAdapter!=null){          mNfcAdapter.enableForegroundDispatch(this,mPendingIntent,null,null);//打开前台发布系统,使页面优于其它nfc处理.当检测到一个Tag标签就会执行mPendingItent}}//页面失去焦点
@Overrideprotected void onPause() {super.onPause();if(mNfcAdapter!=null){mNfcAdapter.disableForegroundDispatch(this);//关闭前台发布系统}}

以上所有操作,都是对一个NFC标签的基本操作,我们封装在一个BaseNfcActivity中,对不同格式标签读写的Activity都继承BaseNfcActivity。

NDEF格式标签读写

我们可以通过Tag对象的getTechList()获取到标签的技术类型,只有支持NDEF格式的标签才可以进行NDEF格式标签的读写操作。

读写NDEF格式标签主要涉及到两个类:

  • NdefMessage:描述NDEF格式的信息,实际上我们写入NFC标签的就是NdefMessage对象。
  • NdefRecord:描述NDEF信息的一个信息段,一个NdefMessage可能包含一个或者多个NdefRecord。

获取Ndef对象

Ndef ndef=Ndef.get(mTag);//获取ndef对象

创建NdefRecord,Android为我们提供了创建NdefRecord的方法,是我们可以轻松创建一个NdefRecord对象

  • NdefRecord.createApplicationRecord(String packageName)
  • NdefRecord.createUri(Uri uri)
  • NdefRecord.createUri(String uriString)
  • NdefRecord.createTextRecord(String languageCode, String text)

遗憾的是NdefRecord.createTextRecord(String languageCode, String text)最小兼容sdk版本是21,对于需要兼容更小版本的应用来说就需要我们自己来实现这个方法。

不管什么格式的数据本质上都是由一些字节组成的。对于NDEF文本格式来说,这些数据的第1个字节描述了数据的状态,然后若干个字节描述文本的语言编码,最后剩余字节表示文本数据。这些数据格式由NFC Forum的相关规范定义,可以通过 http://members.nfc-forum.org/specs/spec_dashboard 下载相关的规范。

NDEF的文本数据规范:

偏移量 长度(bytes) 描述
0 1 状态字节,见下表(状态字节编码格式)
1 n ISO/IANA语言编码。例如”en-US”,”fr-CA”。编码格式是US-ASCII,长度(n)由状态字节的后6位指定。
n+1 m 文本数据。编码格式是UTF-8或UTF-16。编码格式由状态字节的前3位指定。

状态字节编码格式:

字节位(0是最低位,7是最高位) 含义
7 0:文本编码为UTF-8,1:文本编码为UTF-16
6 必须设为0
5..0 语言编码的长度(占用的字节个数)

创建文本NdefRecord

/*** 创建NDEF文本数据* @param text* @return*/public static NdefRecord createTextRecord(String text) {byte[] langBytes = Locale.CHINA.getLanguage().getBytes(Charset.forName("US-ASCII"));Charset utfEncoding = Charset.forName("UTF-8");//将文本转换为UTF-8格式byte[] textBytes = text.getBytes(utfEncoding);//设置状态字节编码最高位数为0int utfBit = 0;//定义状态字节char status = (char) (utfBit + langBytes.length);byte[] data = new byte[1 + langBytes.length + textBytes.length];//设置第一个状态字节,先将状态码转换成字节data[0] = (byte) status;//设置语言编码,使用数组拷贝方法,从0开始拷贝到data中,拷贝到data的1到langBytes.length的位置System.arraycopy(langBytes, 0, data, 1, langBytes.length);//设置文本字节,使用数组拷贝方法,从0开始拷贝到data中,拷贝到data的1 + langBytes.length//到textBytes.length的位置System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);//通过字节传入NdefRecord对象//NdefRecord.RTD_TEXT:传入类型 读写NdefRecord ndefRecord = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,NdefRecord.RTD_TEXT, new byte[0], data);return ndefRecord;}

创建NdefMessage,并且写入Ndef标签

//往Ndef标签中写数据private void writeNdef(){if (mTag==null){Toast.makeText(this,"不能识别的标签类型!",Toast.LENGTH_SHORT);finish();return;}Ndef ndef=Ndef.get(mTag);//获取ndef对象if (!ndef.isWritable()){Toast.makeText(this,"该标签不能写入数据!",Toast.LENGTH_SHORT);return;}NdefRecord ndefRecord=createTextRecord(writeEdt.getText().toString());//创建一个NdefRecord对象NdefMessage ndefMessage=new NdefMessage(new NdefRecord[]{ndefRecord});//根据NdefRecord数组,创建一个NdefMessage对象int size=ndefMessage.getByteArrayLength();if (ndef.getMaxSize()<size){Toast.makeText(this,"标签容量不足!",Toast.LENGTH_SHORT);return;}try {ndef.connect();//连接ndef.writeNdefMessage(ndefMessage);//写数据Toast.makeText(this,"数据写入成功!",Toast.LENGTH_SHORT);} catch (IOException e) {e.printStackTrace();} catch (FormatException e) {e.printStackTrace();}finally {try {ndef.close();//关闭连接} catch (IOException e) {e.printStackTrace();}}}

读Ndef文本数据

//读取Ndef标签中数据private void readNdef(){if (mTag==null){Toast.makeText(this,"不能识别的标签类型!",Toast.LENGTH_SHORT);finish();return;}Ndef ndef=Ndef.get(mTag);//获取ndef对象try {ndef.connect();//连接NdefMessage ndefMessage=ndef.getNdefMessage();//获取NdefMessage对象if (ndefMessage!=null)               readEdt.setText(parseTextRecord(ndefMessage.getRecords()[0]));Toast.makeText(this,"数据读取成功!",Toast.LENGTH_SHORT);} catch (IOException e) {e.printStackTrace();} catch (FormatException e) {e.printStackTrace();}finally {try {ndef.close();//关闭链接} catch (IOException e) {e.printStackTrace();}}}/*** 解析NDEF文本数据,从第三个字节开始,后面的文本数据* @param ndefRecord* @return*/public static String parseTextRecord(NdefRecord ndefRecord) {/*** 判断数据是否为NDEF格式*///判断TNFif (ndefRecord.getTnf() != NdefRecord.TNF_WELL_KNOWN) {return null;}//判断可变的长度的类型if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {return null;}try {//获得字节数组,然后进行分析byte[] payload = ndefRecord.getPayload();//下面开始NDEF文本数据第一个字节,状态字节//判断文本是基于UTF-8还是UTF-16的,取第一个字节"位与"上16进制的80,16进制的80也就是最高位是1,//其他位都是0,所以进行"位与"运算后就会保留最高位String textEncoding = ((payload[0] & 0x80) == 0) ? "UTF-8" : "UTF-16";//3f最高两位是0,第六位是1,所以进行"位与"运算后获得第六位int languageCodeLength = payload[0] & 0x3f;//下面开始NDEF文本数据第二个字节,语言编码//获得语言编码String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");//下面开始NDEF文本数据后面的字节,解析出文本String textRecord = new String(payload, languageCodeLength + 1,payload.length - languageCodeLength - 1, textEncoding);return textRecord;} catch (Exception e) {throw new IllegalArgumentException();}}

MifareClassic格式标签读写

MifareClassic格式标签数据结构 

第一扇区的第一块一般用于制造商占用块

0-15个扇区:一个扇区对应4个块,所以总共有64个块,序号分别为0-63,第一个扇区对应:0-3块,第二个扇区对应:4-7块…

每个扇区的最后一个块用来存放密码或控制位,其余为数据块,一个块占用16个字节,keyA占用6字节,控制位占用4字节,keyB占用6字节。

MifareClassic标签读写常用api:

  • get():根据Tag对象来获得MifareClassic对象;
  • Connect():允许对MifareClassic标签进行IO操作;
  • getType():获得MifareClassic标签的具体类型:TYPE_CLASSIC,TYPE_PLUA,TYPE_PRO,TYPE_UNKNOWN;
  • getSectorCount():获得标签总共有的扇区数量;
  • getBlockCount():获得标签总共有的的块数量;
  • getSize():获得标签的容量:SIZE_1K,SIZE_2K,SIZE_4K,SIZE_MINI
  • authenticateSectorWithKeyA(int SectorIndex,byte[] Key):验证当前扇区的KeyA密码,返回值为ture或false。 常用KeyA:默认出厂密码:KEY_DEFAULT,各种用途的供货商必须配合该技术的MAD:KEY_MIFARE_APPLICATION_DIRECTORY 
    被格式化成NDEF格式的密码:KEY_NFC_FORUM
  • getBlockCountInSector(int):获得当前扇区的所包含块的数量;
  • sectorToBlock(int):当前扇区的第1块的块号;
  • writeBlock(int,data):将数据data写入当前块;注意:data必须刚好是16Byte,末尾不能用0填充,应该用空格
  • readBlock(int):读取当前块的数据。
  • close():禁止对标签的IO操作,释放资源。

写MifareClassic格式标签数据

//写块private void writeBlock(){if (mTag==null){Toast.makeText(this,"无法识别的标签!",Toast.LENGTH_SHORT);finish();return;}if (!haveMifareClissic){Toast.makeText(this,"不支持MifareClassic",Toast.LENGTH_SHORT);finish();return;}MifareClassic mfc=MifareClassic.get(mTag);try {mfc.connect();//打开连接boolean auth;int sector=Integer.parseInt(sectorNum.getText().toString().trim());//写入的扇区int block=Integer.parseInt(blockNum.getText().toString().trim());//写入的块区auth=mfc.authenticateSectorWithKeyA(sector,MifareClassic.KEY_DEFAULT);//keyA验证扇区if (auth){mfc.writeBlock(block,"0123456789012345".getBytes());//写入数据Toast.makeText(this,"写入成功!",Toast.LENGTH_SHORT);}} catch (IOException e) {e.printStackTrace();}finally {try {mfc.close();//关闭连接} catch (IOException e) {e.printStackTrace();}}}

读MifareClassic格式标签数据

//读取块private void readBlock(){if (mTag==null){Toast.makeText(this,"无法识别的标签!",Toast.LENGTH_SHORT);finish();return;}if (!haveMifareClissic){Toast.makeText(this,"不支持MifareClassic",Toast.LENGTH_SHORT);finish();return;}MifareClassic mfc=MifareClassic.get(mTag);try {mfc.connect();//打开连接boolean auth;int sector=Integer.parseInt(sectorNum.getText().toString().trim());//写入的扇区int block=Integer.parseInt(blockNum.getText().toString().trim());//写入的块区auth=mfc.authenticateSectorWithKeyA(sector,MifareClassic.KEY_DEFAULT);//keyA验证扇区if (auth){readData.setText(bytesToHexString(mfc.readBlock(block)));}} catch (IOException e) {e.printStackTrace();}finally {try {mfc.close();//关闭连接} catch (IOException e) {e.printStackTrace();}}}//字符序列转换为16进制字符串private String bytesToHexString(byte[] src) {StringBuilder stringBuilder = new StringBuilder("0x");if (src == null || src.length <= 0) {return null;}char[] buffer = new char[2];for (int i = 0; i < src.length; i++) {buffer[0] = Character.forDigit((src[i] >>> 4) & 0x0F, 16);buffer[1] = Character.forDigit(src[i] & 0x0F, 16);System.out.println(buffer);stringBuilder.append(buffer);}return stringBuilder.toString();}

Android NFC开发-实践篇相关推荐

  1. android nfc ndef mifareclassic,Android NFC开发-实践篇

    Android NFC开发-实践篇 https://blog..net/_GYG/article/details/72899417 在Android NFC开发-理论篇中,我们了解了在Android中 ...

  2. Android NFC开发实战详解

    Android NFC开发实战详解 Android开发实战详解NFC国内第一本AndroidNFC开发书籍带你开启AndroidNFC开发的神秘之旅大综合案例帮助读者快速进入实战角色:WiFi快速连接 ...

  3. Android NFC开发-理论篇

    Android NFC开发-理论篇 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/CSDN_GYG/article/details/72884849 ...

  4. Android NFC开发详细总结

    Android NFC开发详细总结 Near Field Communication (NFC) 为一短距离无线通信技术,通常有效通讯距离为4厘米以内.NFC工作频率为13.65 兆赫兹,通信速率为1 ...

  5. Android NFC开发概述

    NFC手机相比普通手机来说,有以下3个附加功能:  1.可以当成POS机来用,也就是"读取"模式   2.可以当成一张卡来刷,也就是NFC技术最核心的移动支付功能  3.可以像蓝牙 ...

  6. 《Android NFC开发实战详解》——6.4节Android NFC P2P开发进阶

    本节书摘来自异步社区<Android NFC开发实战详解>一书中的第6章,第6.4节Android NFC P2P开发进阶,作者 赵波,更多章节内容可以访问云栖社区"异步社区&q ...

  7. Android NFC开发实战详解PDF

    最近找书做毕设,有的还要加会员注册什么的,下面是<Android NFC开发详解>和<第一行代码>,自取,不用谢,来波点赞呗 链接:https://pan.baidu.com/ ...

  8. android NFC 开发学习笔记(1)

    由于工作需求,最近在研究android nfc开发,借鉴了很对大神的文章在此记录自己的学习过程: 大家学习android开发建议首选android开发文档,该文档在你下载的sdk中,路径:/sdk/d ...

  9. Android NFC 开发实例

    http://blog.csdn.net/pku_android/article/details/7430788 类: Android应用开发系列教程 Android应用开发技巧2012-04-06 ...

最新文章

  1. 运行python需要网吗-python搭建网站(想学Python有什么建议吗?)
  2. boost::hana::append用法的测试程序
  3. linux centos删除安装的包,CentOS yum认为已删除的软件包仍在安装中
  4. zsh和bash的切换,默认shell,alias拼接组合多条命令
  5. 配置中文_《洛克人Zero/ZX遗产合集》PC配置公布 支持中文
  6. singleton 类_在Java中对Singleton类进行双重检查锁定
  7. commons-fileupload实现文件上传下载
  8. 万元大奖,FlyAI算法新赛事,心理卡牌目标检测
  9. SQL必知必会-数据插入
  10. sql组合键设置外键_学习SQL:外键
  11. 拓端tecdat|matlab脉冲响应图的时域特征
  12. PMP培训内容有哪些?
  13. Web渗透测试-实战 方法 思路 总结
  14. 学创客机器人编程材料费贵吗_创客机器人课程容易学习吗
  15. matlab实现单极性,怎样用matlab画单极性非归零码传输码型示意图?请各位大虾赐教...
  16. 形容等待时间长的句子_形容等待很久的句子
  17. android studio连接本地SqlServer数据库报网络错误
  18. pdf做成翻页电子书_想看书就别用手机了,电子书选购指南
  19. php新人笔记,PHP的简单小笔记
  20. java poi打印excel_POI打印Excel报表

热门文章

  1. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之总线设备驱动模型
  2. 内核中接收网络帧的处理
  3. Linux C函数之文件及目录函数
  4. 【C语言】C语言学习整理-putchar,printf,getchar,scanf定义及区别
  5. [计算机系统] - 链接
  6. 菜鸟学习笔记:Java基础篇5(抽象类与接口、回调函数、内部类)
  7. 【JAVA SE】第四章 变量和方法
  8. 力扣529.扫雷游戏
  9. Oracle JOB异常中断原因分析
  10. Java集合框架之Collection实例解析