怀化网站优化公司推荐,wordpress文字转图插件,jf厂高仿手表网站,一浪网站建设文章目录
前言一、什么是NFC#xff1f;二、基础知识1.什么是NDEF#xff1f;2.NFC技术的操作模式3.标签的技术类型4.实现方式的分类5.流程三、获取标签内容1.检查环境2.获取NFC标签2.1 Manifest中注册的方式获取Tag2.1 前台Activity捕获的方式获取Tag四、解析标签数据1. M1…文章目录
前言一、什么是NFC二、基础知识1.什么是NDEF2.NFC技术的操作模式3.标签的技术类型4.实现方式的分类5.流程三、获取标签内容1.检查环境2.获取NFC标签2.1 Manifest中注册的方式获取Tag2.1 前台Activity捕获的方式获取Tag四、解析标签数据1. M1卡解析2. iso15693卡解析总结
一、什么是NFC
NFC是目前Android手机一个主流的配置硬件项全称是Near Field Communication中为近场通信也叫做近距离无线通信技术。使用了NFC技术的设备例如移动电话可以在彼此靠近的情况下进行数据交换是由非接触式射频识别RFID及互连互通技术整合演变而来。
二、基础知识
开始开发之前必须要知道的知识
1.什么是NDEF
存储在NFC标签中的数据可以采用多种格式编写但许多 Android 框架 API 都基于名为 NDEFNFC 数据交换格式的 NFC Forum 标准。。
简单说就是一种普遍的数据格式标准
2.NFC技术的操作模式
1 读取器/写入器模式支持 NFC 设备读取和/或写入被动 NFC 标签和贴纸。 2点对点模式支持 NFC 设备与其他 NFC 对等设备交换数据Android Beam 使用的就是此操作模式。 3卡模拟模式支持 NFC 设备本身充当 NFC 卡。然后可以通过外部 NFC 读取器例如 NFC 销售终端访问模拟 NFC 卡。
本篇案例使用的主要是读写卡就是正常的读写卡需求后面如果有机会接触到点对点和卡模拟的需求会在此篇做补充
3.标签的技术类型
通常情况下每种分类的标签卡片都支持一种或多重技术 对应关系如下
技术描述卡种NfcA提供NFC-AISO 14443-3A的性能和I / O操作的访问。M1卡NfcB提供NFC-B (ISO 14443-3B)的性能和I / O操作的访问。NfcF提供 NFC-F (JIS 6319-4)的性能和I / O操作的访问。NfcV提供 NFC-V (ISO 15693)的性能和I / O操作的访问。15693卡IsoDep提供 ISO-DEP (ISO 14443-4)的性能和I / O操作的访问。CPU卡Ndef提供NFC标签已被格式化为NDEF的数据和操作的访问。NdefFormatable提供可能被格式化为NDEF的 formattable的标签。MifareClassic如果此Android设备支持MIFARE,提供访问的MIFARE Classic性能和I / O操作。m1卡MifareUltralight如果此Android设备支持MIFARE,提供访问的MIFARE 超轻性能和I / O操作。
如下图这是Demo 显示得NFC标签的信息。 其中被我圈起来的部分是这个NFC标签支持的技术这些后面解析数据的时候会用到得到这些后就可以使用对应的类来解析标签数据。 开发中我们有对应的方法来获取此标签支持的解析方式后面我会介绍。
4.实现方式的分类
1Manifest注册方式这种方式主要是在Manifest文件对应的activity下配置过滤器以响应不同类型NFC Action。使用这种方式在刷卡时如果手机中有多个应用都存在该NFC实现方案系统会弹出能响应NFC事件的应用列表供用户选择用户需要点击目标应用来响应本次NFC刷卡事件。
2前台响应方式无需Manifest重配置过滤器直接使用前台activity来捕获NFC事件进行响应。 区别如下 响应方式不同Manifest注册的NFC事件由系统分发需要选择应用去响应事件 前台响应方式由前台activity来捕获NFC事件进行响应 优先级不同前台响应方式的优先级更高于Manifest注册的方式 如果安装多个Manifest注册的的App 和一个处于前台捕获方式的App刷卡后 优先级最高的为前台捕获的如果前台相应方式的App没有打开那么将弹出列表让用户选择Manifest中注册了的符合条件的App
第一种更适合APP需要刷卡调用起来并且设备没有多个响应NFC标签程序的物联网设备因为普通安卓手机中自带的卡包APP、微信等优先级都比较高,当弹出列表选择响应的App时操作会边得繁琐
第二种更适合前台界面中的读卡且多个应用的时候根据自己的项目需求选择适合的实现方式。
5.流程
首先设备要支持NFC权限开启的前提下 不论哪种方式都是先刷卡等待系统分发响应的Activity 拿到Tag或者 前台Activity捕获到TAG 。然后根据这个标签支持的技术去解析数据。
三、获取标签内容
1.检查环境
首先 Manifest中添加权限 uses-permission android:nameandroid.permission.NFC /判断是否支持NFC、且打开功能 NfcAdapter adapter NfcAdapter.getDefaultAdapter(this);if (null adapter) {Toast.makeText(this, 不支持NFC功能, Toast.LENGTH_SHORT).show();} else if (!adapter.isEnabled()) {Intent intent new Intent(Settings.ACTION_NFC_SETTINGS);// 根据包名打开对应的设置界面startActivity(intent);}
2.获取NFC标签
2.1 Manifest中注册的方式获取Tag
这里要介绍三种意图过滤器 前面【实现方式的分类】中对这种方式的特征做了介绍这种由标签调度系统分发的方式需要在Manifest定义固定的意图过滤器。标签调度系统定义了三种 Intent按优先级从高到低列出如下
ACTION_NDEF_DISCOVERED如果扫描到包含 NDEF 负载的标签并且可识别其类型则使用此 Intent 启动 Activity。这是优先级最高的 Intent标签调度系统会尽可能尝试使用此 Intent 启动 Activity在行不通时才会尝试使用其他 Intent。 ACTION_TECH_DISCOVERED如果没有登记要处理 ACTION_NDEF_DISCOVERED Intent 的 Activity则标签调度系统会尝试使用此 Intent 来启动应用。此外如果扫描到的标签包含无法映射到 MIME 类型或 URI 的 NDEF 数据或者该标签不包含 NDEF 数据但它使用了已知的标签技术那么也会直接启动此 Intent无需先启动 ACTION_NDEF_DISCOVERED。
ACTION_TAG_DISCOVERED如果没有处理 ACTION_NDEF_DISCOVERED 或者 ACTION_TECH_DISCOVERED Intent 的 Activity则使用此 Intent 启动 Activity。
添加意图过滤器 这是第一种 最简单和优先级最高的一种已经满足需求了 activityandroid:name.NfcActivityandroid:exportedfalseintent-filteraction android:nameandroid.nfc.action.NDEF_DISCOVERED //intent-filter/activity当然也可以选择第二种 activityandroid:name.NfcActivityandroid:exportedfalseintent-filteraction android:nameandroid.nfc.action.TECH_DISCOVERED //intent-filtermeta-data android:nameandroid.nfc.action.TECH_DISCOVEREDandroid:resourcexml/filter_nfc //activityfilter_nfc 这个文件就是TECH_DISCOVERED需要配置的其中tech-list之间是逻辑或关系tech之间是逻辑与关系与方案2中的techLists原理以及用途是类似。 ?xml version1.0 encodingutf-8?
resources xmlns:androidhttp://schemas.android.com/apk/res/androidtech-listtechandroid.nfc.tech.Ndef/techtechandroid.nfc.tech.NfcA/tech/tech-listtech-listtechandroid.nfc.tech.NfcB/tech/tech-listtech-listtechandroid.nfc.tech.NfcF/tech/tech-list
/resources还剩最后一种 activityandroid:name.NfcActivityandroid:exportedfalseintent-filteraction android:nameandroid.nfc.action.TAG_DISCOVERED/category android:nameandroid.intent.category.DEFAULT//intent-filter/activity这种一般用不到 感觉意义不大
然后在对应Activity的onCreate方法中就可以拿标签了
class NfcActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_nfc)val adapter NfcAdapter.getDefaultAdapter(this)if (null adapter) {Toast.makeText(this, 不支持NFC功能, Toast.LENGTH_SHORT).show()} else if (!adapter.isEnabled) {val intent Intent(Settings.ACTION_NFC_SETTINGS)// 根据包名打开对应的设置界面startActivity(intent)}val tag intent.getParcelableExtraTag(NfcAdapter.EXTRA_TAG)}
}2.1 前台Activity捕获的方式获取Tag
class MainActivity : AppCompatActivity() {var mNfcAdapter: NfcAdapter? nullvar pIntent: PendingIntent? nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)initNfc()}private fun initNfc() {mNfcAdapter M1CardUtils.isNfcAble(this)pIntent PendingIntent.getActivity(this, 0, //在Manifest里或者这里设置当前activity启动模式否则每次响应NFC事件activity会重复创建Intent(this, javaClass).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0)}override fun onResume() {super.onResume()mNfcAdapter?.let {val ndef IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED)val tag IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED)val tech IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED)val filters arrayOf(ndef, tag, tech)val techList arrayOf(arrayOf(android.nfc.tech.Ndef,android.nfc.tech.NfcA,android.nfc.tech.NfcB,android.nfc.tech.NfcF,android.nfc.tech.NfcV,android.nfc.tech.NdefFormatable,android.nfc.tech.MifareClassic,android.nfc.tech.MifareUltralight,android.nfc.tech.NfcBarcode))it.enableForegroundDispatch(this, pIntent, filters, techList)XLog.d(开始捕获NFC数据)}}override fun onPause() {super.onPause()mNfcAdapter?.disableForegroundDispatch(this)}override fun onNewIntent(intent: Intent?) {super.onNewIntent(intent)//这里必须setIntentset NFC事件响应后的intent才能拿到数据setIntent(intent)val tag getIntent().getParcelableExtraTag(NfcAdapter.EXTRA_TAG)//M1CardUtils 我后面会贴出来的if (M1CardUtils.isMifareClassic(tag)) {try {val reader M1CardUtils.readCard(tag)XLog.d(读卡内容$reader)val data reader.split(|)} catch (e: IOException) {e.printStackTrace()}}}
}四、解析标签数据
不论使用哪种方式当我们获取到TAG标签后解析方式都是相同的,需要根据不同的卡类型选择对应的解析方式 如图 我们能拿到卡片的信息如图括起来的部分分别对应的是 支持的技术类型 MifareClassic 类型 扇区存储空间 扇区数 扇区中的块数
1. M1卡解析
这里说一下基础知识不论是NFC还是读卡模块读解析流程都是先寻卡然后验证扇区的密码取扇区的数据比如已知要读的数据在2扇区那么寻卡后验证时把要验证的扇区号、扇区的密码和扇区的验证密码类型A/B传过去验证通过后就可以读取数据了。 import android.app.Activity
import android.nfc.NfcAdapter
import android.nfc.Tag
import com.hjq.toast.ToastUtils
import kotlin.Throws
import android.nfc.tech.MifareClassic
import com.elvishew.xlog.XLog
import java.io.IOException
import java.lang.StringBuilder
import java.nio.charset.Charsetobject M1CardUtils {/*** 判断是否支持NFC** return*/fun isNfcAble(mContext: Activity?): NfcAdapter? {val mNfcAdapter NfcAdapter.getDefaultAdapter(mContext)if (mNfcAdapter null) {ToastUtils.show(设备不支持NFC)}if (!mNfcAdapter!!.isEnabled) {ToastUtils.show(请在系统设置中先启用NFC功能)}return mNfcAdapter}/*** 监测是否支持MifareClassic** param tag* return*/fun isMifareClassic(tag: Tag): Boolean {val techList tag.techListvar haveMifareUltralight falsefor (tech in techList) {if (tech.contains(MifareClassic)) {haveMifareUltralight truebreak}}if (!haveMifareUltralight) {ToastUtils.show(不支持MifareClassic)return false}return true}/*** 读取卡片信息** return*/Throws(IOException::class)fun readCard(tag: Tag?): String {val mifareClassic MifareClassic.get(tag)return try {mifareClassic.connect()val metaInfo StringBuilder()val gbk Charset.forName(gbk)// 获取TAG中包含的扇区数val sectorCount mifareClassic.sectorCount// for (int j 0; j sectorCount; j) {val bCount: Int //当前扇区的块数var bIndex: Int //当前扇区第一块if (m1Auth(mifareClassic, 2)) {bCount mifareClassic.getBlockCountInSector(2)bIndex mifareClassic.sectorToBlock(2)var length 0for (i in 0 until bCount) {val data mifareClassic.readBlock(bIndex)for (i1 in data.indices) {if (data[i1] 0.toByte()) {length i1}}val dataString String(data, 0, length, gbk).trim { it }metaInfo.append(dataString)bIndex}} else {XLog.e(密码校验失败)}// }metaInfo.toString()} catch (e: IOException) {throw IOException(e)} finally {try {mifareClassic.close()} catch (e: IOException) {throw IOException(e)}}}/*** 改写数据** param block* param blockbyte*/Throws(IOException::class)fun writeBlock(tag: Tag?, block: Int, blockbyte: ByteArray?): Boolean {val mifareClassic MifareClassic.get(tag)try {mifareClassic.connect()if (m1Auth(mifareClassic, block / 4)) {mifareClassic.writeBlock(block, blockbyte)XLog.e(writeBlock, 写入成功)} else {XLog.e(密码是, 没有找到密码)return false}} catch (e: IOException) {throw IOException(e)} finally {try {mifareClassic.close()} catch (e: IOException) {throw IOException(e)}}return true}/*** 密码校验** param mTag* param position* return* throws IOException*/Throws(IOException::class)fun m1Auth(mTag: MifareClassic, position: Int): Boolean {if (mTag.authenticateSectorWithKeyA(position, MifareClassic.KEY_DEFAULT)) {return true} else if (mTag.authenticateSectorWithKeyB(position, MifareClassic.KEY_DEFAULT)) {return true}return false}}2. iso15693卡解析
本案例中没有用到这种只是需要M1所以不需要这个这是别的大佬封装的类发出来供参考
import android.nfc.tech.NfcV;import com.haiheng.core.util.ByteUtils;import java.io.IOException;/*** NfcV(ISO 15693)读写操作* 用法* NfcV mNfcV NfcV.get(tag);* mNfcV.connect();* p* NfcVUtils mNfcVutil new NfcVUtils(mNfcV);* 取得UID* mNfcVutil.getUID();* 读取block在1位置的内容* mNfcVutil.readOneBlock(1);* 从位置7开始读2个block的内容* mNfcVutil.readBlocks(7, 2);* 取得block的个数* mNfcVutil.getBlockNumber();* 取得1个block的长度* mNfcVutil.getOneBlockSize();* 往位置1的block写内容* mNfcVutil.writeBlock(1, new byte[]{0, 0, 0, 0})** author Kelly* version 1.0.0* filename NfcVUtils.java* time 2018/10/30 10:29* copyright(C) 2018 song*/
public class NfcVUtils {private NfcV mNfcV;/*** UID数组行式*/private byte[] ID;private String UID;private String DSFID;private String AFI;/*** block的个数*/private int blockNumber;/*** 一个block长度*/private int oneBlockSize;/*** 信息*/private byte[] infoRmation;/*** * 初始化* * param mNfcV NfcV对象* * throws IOException* */public NfcVUtils(NfcV mNfcV) throws IOException {this.mNfcV mNfcV;ID this.mNfcV.getTag().getId();byte[] uid new byte[ID.length];int j 0;for (int i ID.length - 1; i 0; i--) {uid[j] ID[i];j;}this.UID ByteUtils.byteArrToHexString(uid);getInfoRmation();}public String getUID() {return UID;}/*** * 取得标签信息 * */private byte[] getInfoRmation() throws IOException {byte[] cmd new byte[10];cmd[0] (byte) 0x22; // flagcmd[1] (byte) 0x2B; // commandSystem.arraycopy(ID, 0, cmd, 2, ID.length); // UIDinfoRmation mNfcV.transceive(cmd);blockNumber infoRmation[12];oneBlockSize infoRmation[13];AFI ByteUtils.byteArrToHexString(new byte[]{infoRmation[11]});DSFID ByteUtils.byteArrToHexString(new byte[]{infoRmation[10]});return infoRmation;}public String getDSFID() {return DSFID;}public String getAFI() {return AFI;}public int getBlockNumber() {return blockNumber 1;}public int getOneBlockSize() {return oneBlockSize 1;}/*** * 读取一个位置在position的block* * param position 要读取的block位置* * return 返回内容字符串* * throws IOException* */public String readOneBlock(int position) throws IOException {byte cmd[] new byte[11];cmd[0] (byte) 0x22;cmd[1] (byte) 0x20;System.arraycopy(ID, 0, cmd, 2, ID.length); // UIDcmd[10] (byte) position;byte res[] mNfcV.transceive(cmd);if (res[0] 0x00) {byte block[] new byte[res.length - 1];System.arraycopy(res, 1, block, 0, res.length - 1);return ByteUtils.byteArrToHexString(block);}return null;}/*** * 读取从begin开始end个block* * begin count 不能超过blockNumber* * param begin block开始位置* * param count 读取block数量* * return 返回内容字符串* * throws IOException* */public String readBlocks(int begin, int count) throws IOException {if ((begin count) blockNumber) {count blockNumber - begin;}StringBuffer data new StringBuffer();for (int i begin; i count begin; i) {data.append(readOneBlock(i));}return data.toString();}/*** * 将数据写入到block,* * param position 要写内容的block位置* * param data 要写的内容,必须长度为blockOneSize* * return false为写入失败true为写入成功* * throws IOException * */public boolean writeBlock(int position, byte[] data) throws IOException {byte cmd[] new byte[15];cmd[0] (byte) 0x22;cmd[1] (byte) 0x21;System.arraycopy(ID, 0, cmd, 2, ID.length); // UID//blockcmd[10] (byte) position;//valueSystem.arraycopy(data, 0, cmd, 11, data.length);byte[] rsp mNfcV.transceive(cmd);if (rsp[0] 0x00)return true;return false;}
}总结
以上就是今天要讲的内容文章中如有错误或者需要改进的地方欢迎补充指正本文仅介绍了NFC的使用和M1卡的读取解析场景关于NFC的历史、卡片类型、Intent filter类型详细描述其他使用场景等可以参考更多文档这里贴出来几个我看到的对我很有帮助的文章也欢迎大家多做参考 NFC 各种卡类型、区别、历史介绍 https://zhuanlan.zhihu.com/p/344426747 各种官方资料中文说明 https://blog.csdn.net/u013164293/article/details/124474247?spm1001.2014.3001.5506