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

Android NFC开发-实践篇

https://blog..net/_GYG/article/details/72899417

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

基本操作

配置AndroidMenifest.xml:

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

@Override

protected 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对象

@Override

protected 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失去焦点时,关闭前台发布系统

//页面获取焦点

@Override

protected void onResume() {

super.onResume();

if (mNfcAdapter!=null){ mNfcAdapter.enableForegroundDispatch(this,mPendingIntent,null,null);//打开前台发布系统,使页面优于其它nfc处理.当检测到一个Tag标签就会执行mPendingItent

}

}

//页面失去焦点

@Override

protected 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);

//设置状态字节编码最高位数为0

int 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()

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格式

*/

//判断TNF

if (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格式标签数据结构

0dd636741e75d5a1c6722ff01377d098.png

0a635ad7edf585303c5837bf1edd1c00.png

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

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();

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/559600.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

苹果6换屏多钱_苹果手机屏幕碎了怎么办?维修更换要多少钱

手机在使用过程中最容易发生的意外就是手滑摔碎屏幕了&#xff0c;那么苹果手机屏幕碎了碎了&#xff0c;维修更换要多少钱&#xff1f;大家都知道&#xff0c;苹果手机摔坏&#xff0c;进水均属于人为损坏&#xff0c;人为损坏不属保修范围&#xff0c;接下来针对苹果手机屏幕…

harmonyos sdk,HarmonyOS SDK对应的API版本跃迁引发的历史工程适配问题解决方案

历史工程自动适配由于最新版本的HarmonyOS SDK对应的API Version发生了跃迁&#xff0c;原有的API Version 3变成了当前的API Version 4&#xff0c;原有的API Version 4变成了当前的API Version 5。因此&#xff0c;使用最新版本的DevEco Studio打开历史工程&#xff0c;需要对…

dataframe 拼接_拼接关系图在石材生产过程中的重要性

石材生产加工中应用许多图&#xff0c;石材纹理图、平面面置图、平面图、立面图、剖面图&#xff0c;这些图对石材生产加工都有很大的帮助&#xff0c;发挥着各自的作用&#xff0c;担负着各自的角色。除了这些图外&#xff0c;石材生产加工中还有一种图—拼接关系图&#xff0…

app名字变为android+api,一起来做个app吧 wanandroid开放API

由于早期开放的一些API页码为0开始&#xff0c;后期接口修改为从1开始&#xff0c;为了兼顾之前的开放API&#xff0c;故无法统一。对于POST接口建议使用postman模拟在编写过程中如果遇到一些问题&#xff0c;也有一些参考项目&#xff0c;这里针对Java和Kotlin各自选择了一款&…

php 武汉海关对接_“双11”临近 海口海关全力备战跨境电商监管高峰

中新网海南新闻11月6日电(李佳臣)海口海关6日发布消息称&#xff0c;面对“双11”这一中国电商行业的年度盛事&#xff0c;海口马村港海关已做好准备&#xff0c;确保“双11”期间跨境电商业务24小时即时通关&#xff0c;包裹通关、出区“零等待”。为迎接即将到来的“双11”网…

嵌入式全栈工程师_我花了半个月,整理出了这篇嵌入式开发学习指南(学习路线+知识点梳理)...

不好意思久等了这篇文章让小伙伴们久等了。一年多以来&#xff0c;关于嵌入式开发学习路线、规划、看什么书等问题&#xff0c;被问得没有一百&#xff0c;也有大几十次了。但是无奈自己对这方面了解有限&#xff0c;所以每次都没法交代&#xff0c;搞得实在不好意思。但是办法…

java面试题_1000道Java工程师面试题+答案PDF485页

说实话&#xff0c;作为一名 Java 程序员&#xff0c;不论你需不需要面试都应该好好看下这份资料。我大概撸了一遍&#xff0c;真的是堪称典范。就目前国内的面试模式来讲&#xff0c;在面试前积极的准备面试&#xff0c;复习整个 Java 知识体系将变得非常重要&#xff0c;可以…

html5小游戏是用js做的吗,谁说做H5动画和游戏一定要canvas?

2021.05.06-更新了底层&#xff0c;使用起来更加方便&#xff0c;还可以使用oop的继承&#xff0c;增加了一个新demo/----------------------------------------以下原文内容------------------------------------------/记得上上个礼拜&#xff0c;我在createjs的技术群里说了…

单变量和多变量财务预警模型_SPSS数据分析,基于判别分析上市公司财务危机预警分析...

研究概述财务危机(Financial crisis)又称财务困境(Financial distress)&#xff0c;是指企业由于营销、决策或不可抗拒因素的影响&#xff0c;使经营循环和财务循环无法正常持续或陷于停滞的状态&#xff0c;具体表现包括持续性亏损、无偿付能力、违约和破产等。研究意义财务危…

先装vs还是先装sql_锅炉给水泵的止回阀到底安装在出口阀前还是阀后?

今天我们来讨论一下锅炉给水泵止回阀安装位置。那么止回阀的安装位置如何确定呢&#xff1f;泵前安装与泵后安装止回阀有何区别&#xff0c;泵前安装适用于哪些地方&#xff1f;止回阀通常要配合其他阀门一起使用&#xff0c;那么跟其他阀门配合使用时&#xff0c;止回阀要安装…

mosek 安装配置python_python安装、配置以及pyinstaller的安装、使用

一、Python下载https://www.python.org/downloads/windows/ 根据自己的需要下载所需的版本二、Python安装可直接点“Install Now”&#xff0c;注意勾选最下面“Add Python 3.6 to PATH”&#xff0c;不然要配置环境。等一会边安装成功&#xff0c;点“close”在开始菜单里打开…

腾讯x5加载本地html乱码,腾讯X5内核播放器遇到的问题

最近在写一个和视频有关的项目&#xff0c;用到了腾讯x5内核的webview。利用webview调用本地js文件来播放视频。事情本身很顺利&#xff0c;但是在过程中遇到了一些小插曲&#xff0c;在此记录一下。1.去除播放器中的广告正常使用的情况下&#xff0c;我们进入播放界面会出现如…

c++创建文件_使用Python实现文件压缩和解压

(点击上方快速关注并设置为星标&#xff0c;一起学Python)来源&#xff1a;网络大家可能都熟悉.zip格式的文件。它可以把多个文件&#xff0c;压缩成一个文件。这在网络上传输时很有用&#xff0c;而且节省硬盘空间。接下来&#xff0c;我们使用Python实现压缩和解压。1、读取Z…

猜拳游戏html,JavaScript中实现猜拳小游戏

页面布局html{font-size: 125%;margin: 0rem;}.wap-main{background: -webkit-linear-gradient(right,#7e2b9a,#ac3e34); /*safari 5.1 to 6.0*/background: -o-linear-gradient(right,#7e2b9a,#ac3e34); /*opera 11.1 to 12.0*/background: -moz-linear-gradient(right,#7e2b9…

thymealf如何实现传单个变量给html_纯前端使用JavaScript发送电子邮件,5个步骤图文教程...

你不需要使用任何后端语言&#xff0c;如 PHP 或 Python。此外&#xff0c;你甚至不需要Node.js!有很多方法可以读取这些数据。你可以将你的表单与数据库(如MySQL)连接&#xff0c;然后从数据库中读取传入的信息。好吧&#xff0c;这是一个选择&#xff0c;但是我认为这对于你的…

rem布局 html,移动端h5之rem布局/px2rem

rem布局之媒体匹配最早的时候用的rem适配方法&#xff0c;通过手动设置媒体查询对不同设备进行设置font-size// 自适应// ------------------------html{font-size: 38px;}media only screen and (min-width: 320px) {html {font-size: 42.666px !important;}}media only scree…

消息已读未读的模型设计_阿里云技术专家分享:现代 IM 系统中消息推送和存储架构的实现...

前言IM 全称是“Instant Messaging”&#xff0c;中文名是即时通讯。在这个高度信息化的移动互联网时代&#xff0c;生活中 IM 类产品已经成为必备品&#xff0c;比较有名的如钉钉、微信、QQ 等以 IM 为核心功能的产品。当然目前微信已经成长为一个生态型产品&#xff0c;但其核…

移动端html5广告的优势,h5手机端开发的优势都有哪些呢

原标题&#xff1a;h5手机端开发的优势都有哪些呢现在是手机不离手的时代&#xff0c;可以说每个人都有一部甚至两部手机来打发日常的空余时间&#xff0c;那么你知道h5手机端开发的优势都有哪些吗?下面原创先锋小编给大家详细介绍下&#xff0c;想要了解的朋友一起来看看吧。…

园林景观cad_自学CAD太难?送你550张练习图纸,七天时间小白蜕变成大神

自学CAD太难&#xff1f;送你550张练习图纸&#xff0c;七天时间小白蜕变成大神俗话说&#xff1a;实践是检验真理的唯一标准。对于想要熟练CAD的朋友来说&#xff0c;最重要的就是练习&#xff01;大量的练习&#xff01;CAD画图是个熟能生巧的事情&#xff0c;练多了&#xf…

面条html5,使用 babel 全家桶模块化古老的面条代码

在最近的工作中&#xff0c;接手了一个古老的项目&#xff0c;其中的 JS 代码是一整坨的面条代码&#xff0c;约 3000 行的代码全写在一个文件里&#xff0c;维护起来着实让人头疼。想不通为啥之前维护项目的同学能够忍受这么难以维护的代码……既然现在这个锅被我拿下了&#…