Android NFC开发详解:NFC读卡实例解析及总结

06-01 832阅读

文章目录

  • 前言
  • 一、什么是NFC?
  • 二、基础知识
  • 1.什么是NDEF?
  • 2.NFC技术的操作模式
  • 3.标签的技术类型
  • 4.实现方式的分类
  • 5.流程
  • 三、获取标签内容
  • 1.检查环境
  • 2.获取NFC标签
  • 2.1 Manifest中注册的方式获取Tag
  • 2.1 前台Activity捕获的方式获取Tag
  • 四、解析标签数据
  • 1. M1卡解析
  • 2. iso15693卡解析
  • 总结

    一、什么是NFC?

    NFC是目前Android手机一个主流的配置硬件项,全称是Near Field Communication,中为近场通信,也叫做近距离无线通信技术。使用了NFC技术的设备(例如移动电话)可以在彼此靠近的情况下进行数据交换,是由非接触式射频识别(RFID)及互连互通技术整合演变而来。

    二、基础知识

    开始开发之前必须要知道的知识

    1.什么是NDEF?

    存储在NFC标签中的数据可以采用多种格式编写,但许多 Android 框架 API 都基于名为 NDEF(NFC 数据交换格式)的 NFC Forum 标准。。

    简单说就是一种普遍的数据格式标准

    2.NFC技术的操作模式

    (1) 读取器/写入器模式:支持 NFC 设备读取和/或写入被动 NFC 标签和贴纸。

    (2)点对点模式:支持 NFC 设备与其他 NFC 对等设备交换数据;Android Beam 使用的就是此操作模式。

    (3)卡模拟模式:支持 NFC 设备本身充当 NFC 卡。然后,可以通过外部 NFC 读取器(例如 NFC 销售终端)访问模拟 NFC 卡。

    本篇案例使用的主要是读写卡,就是正常的读写卡需求,后面如果有机会接触到点对点和卡模拟的需求会在此篇做补充

    3.标签的技术类型

    通常情况下每种分类的标签(卡片)都支持一种或多重技术,

    对应关系如下

    技术描述卡种
    NfcA提供NFC-A(ISO 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标签支持的技术,这些后面解析数据的时候会用到,得到这些后就可以使用对应的类来解析标签数据。

     

    Android NFC开发详解:NFC读卡实例解析及总结

    开发中我们有对应的方法来获取此标签支持的解析方式,后面我会介绍。

    4.实现方式的分类

    (1)Manifest注册方式:这种方式主要是在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中添加权限

        
    

    判断是否支持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。

    添加意图过滤器

    这是第一种 最简单和优先级最高的一种,已经满足需求了

            
                
                    
                
            
    

    当然也可以选择第二种

            
                
                    
                
                
            
    

    filter_nfc

    这个文件就是TECH_DISCOVERED需要配置的,其中,tech-list之间是逻辑或关系,tech之间是逻辑与关系,与方案2中的techLists原理以及用途是类似。

    
     
        
            android.nfc.tech.Ndef
            android.nfc.tech.NfcA
        
     
        
            android.nfc.tech.NfcB
        
        
            android.nfc.tech.NfcF
        
    
    

    还剩最后一种

            
                
                    
                    
                
            
    

    这种一般用不到 感觉意义不大

    然后在对应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.getParcelableExtra(NfcAdapter.EXTRA_TAG)
        }
    }
    

    2.1 前台Activity捕获的方式获取Tag

    class MainActivity : AppCompatActivity() {
        var mNfcAdapter: NfcAdapter? = null
        var pIntent: PendingIntent? = null
        override 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)
            //这里必须setIntent,set  NFC事件响应后的intent才能拿到数据
            setIntent(intent)
            val tag = getIntent().getParcelableExtra(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标签后,解析方式都是相同的,需要根据不同的卡类型选择对应的解析方式

     

    Android NFC开发详解:NFC读卡实例解析及总结

    如图 我们能拿到卡片的信息,如图,括起来的部分分别对应的是:

    支持的技术类型

    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.Charset
    object 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.techList
            var haveMifareUltralight = false
            for (tech in techList) {
                if (tech.contains("MifareClassic")) {
                    haveMifareUltralight = true
                    break
                }
            }
            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  blockNumber) {
                count = blockNumber - begin;
            }
            StringBuffer data = new StringBuffer();
            for (int i = begin; i  
    

    总结

    以上就是今天要讲的内容,文章中如有错误或者需要改进的地方欢迎补充指正,本文仅介绍了NFC的使用和M1卡的读取解析场景,关于NFC的历史、卡片类型、Intent filter类型详细描述,其他使用场景等可以参考更多文档,这里贴出来几个我看到的对我很有帮助的文章,也欢迎大家多做参考,

    NFC 各种卡类型、区别、历史介绍

    https://zhuanlan.zhihu.com/p/344426747

    各种官方资料中文说明

    https://blog.csdn.net/u013164293/article/details/124474247?spm=1001.2014.3001.5506

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码