ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Android开发实现NFC刷卡读取的两种方式

2022-07-23 01:02:42  阅读:422  来源: 互联网

标签:src NFC 读取 tech nfc Android intent android


Android开发实现NFC刷卡读取的两种方式

 更新时间:2021年09月18日 11:54:13   作者:TheFlashArrow     这篇文章主要为大家详细介绍了Android开发中实现NFC刷卡读取的两种方式,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下  

场景:NFC是目前Android手机一个主流的配置硬件项,本文主要讲解一下Android开发中,NFC刷卡的两种实现方式以及相关方法源码解析。

①:Manifest注册方式:这种方式主要是在Manifest文件对应的activity下,配置过滤器,以响应不同类型NFC  Action。使用这种方式,在刷卡时,如果手机中有多个应用都存在该NFC实现方案,系统会弹出能响应NFC事件的应用列表供用户选择,用户需要点击目标应用来响应本次NFC刷卡事件。目前我公司这边项目中使用了该逻辑,比较简便,这里先贴一下该方式的实现逻辑。

Manifest配置:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <!--权限要加,这是一个普通权限,不需要动态申请,但是在小米手机里需要动态申请-->      <uses-permission android:name="android.permission.NFC" />           <uses-feature         android:name="android.hardware.nfc"         android:required="false" />         <application>         ...           <activity             android:name=".NfcActivity"             android:launchMode="singleTask"             android:screenOrientation="portrait"             android:theme="@android:style/Theme.Translucent">             <!--透明主题,把刷卡变成一个无感知的过程-->             <intent-filter>                 <action android:name="android.nfc.action.NDEF_DISCOVERED" />             </intent-filter>             <intent-filter>                 <action android:name="android.nfc.action.TAG_DISCOVERED" />                    <category android:name="android.intent.category.DEFAULT" />             </intent-filter>             <intent-filter>                 <action android:name="android.nfc.action.TECH_DISCOVERED" />                 <!--使用这个过滤器 这里其实还要用 meta-data 配置一下标签过滤,-->                 <!--我项目中是 NDEF_DISCOVERED 这个TECH_DISCOVERED形同虚设-->             </intent-filter>             <meta-data                 android:name="android.nfc.action.TECH_DISCOVERED"                 android:resource="@xml/nfc_tech" />         </activity> </application>

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="utf-8"?> <resources xmlns:android="http://schemas.android.com/apk/res/android">        <tech-list>         <tech>android.nfc.tech.Ndef</tech>         <tech>android.nfc.tech.NfcA</tech>     </tech-list>        <tech-list>         <tech>android.nfc.tech.NfcB</tech>     </tech-list>     <tech-list>         <tech>android.nfc.tech.NfcF</tech>     </tech-list> </resources>

NfcActivity:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 public class NfcActivity extends Activity {        @Override     protected void onCreate(@Nullable Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_nfc);         initData();     }        /**      * 初始化数据      */     private void initData() {         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);         }            //我项目中是拿了NFC卡的tag中的id数据,这根据具体情况来;         // 可以在NfcAdapter源码中查看,具体能拿到哪些数据         Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);         String id = bytesToHex(tag.getId());            //TODO 目前我这边项目中,拿到数据后,通过EventBus分发到对应的activity,当然也能使用其他分发响应方式,            //关闭动画,毕竟对用户来说,刷卡应当是一个无感知的过程         overridePendingTransition(0, 0);         finish();     }        @Override     protected void onNewIntent(Intent intent) {         super.onNewIntent(intent);         initData();     }        /**      *  2转10      * @param src      * @return      */     private static String bytesToTenNum(byte[] src) {         StringBuilder stringBuilder = new StringBuilder();         if (src == null || src.length <= 0) {             return null;         }         char[] buffer = new char[2];         for (int i = 0; i < src.length; i++) {             buffer[1] = Character.toUpperCase(Character.forDigit(                     (src[i] >>> 4) & 0x0F, 16));             buffer[0] = Character.toUpperCase(Character.forDigit(src[i] & 0x0F,                     16));             stringBuilder.append(buffer);         }         stringBuilder.reverse();         BigInteger bigi = new BigInteger(stringBuilder.toString(), 16);         return bigi.toString();     }        /**      * 2转16      * @param src      * @return      */     private static String bytesToHex(byte[] src){         StringBuffer sb = new StringBuffer();         if (src == null || src.length <= 0) {             return null;         }         String sTemp;         for (int i = 0; i < src.length; i++) {             sTemp = Integer.toHexString(0xFF & src[i]);             if (sTemp.length() < 2){                 sb.append(0);             }             sb.append(sTemp.toUpperCase());         }         return sb.toString();     } }

②:前台响应机制:这种方式与第一种的区别如下:方法一中,NFC事件由系统分发,需要选择应用去响应事件;而方法二,直接使用前台activity来捕获NFC事件进行响应,并且优先级高于方案一。

下面对该方案进行解析,直接怼上代码。这里我新建了一个NfcTestActivity进行测试,布局文件就补贴了,随便丢一个就行。

NfcTestActivity:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 /**  * @author Flash  * 创建时间:2021-07-30 11:14  */ public class NfcTestActivity extends AppCompatActivity {        NfcAdapter mNfcAdapter;     PendingIntent pIntent;        @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_nfc_test);         initNfc();         Log.i("FlashTestNFC", "onCreate");     }        @Override     protected void onStop() {         super.onStop();         Log.i("FlashTestNFC", "onStop");     }        @Override     protected void onDestroy() {         super.onDestroy();         Log.i("FlashTestNFC", "onDestroy");     }        /**      * 初始化      */     private void initNfc(){         mNfcAdapter = NfcAdapter.getDefaultAdapter(this);         pIntent = PendingIntent.getActivity(this, 0,                 //在Manifest里或者这里设置当前activity启动模式,否则每次向阳NFC事件,activity会重复创建                 // 当然也要按照具体情况来,你设置成singleTask也不是不行,                 new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),                 0);     }        @Override     protected void onNewIntent(Intent intent) {         super.onNewIntent(intent);         //这里必须setIntent,set  NFC事件响应后的intent才能拿到数据         setIntent(intent);         Log.i("FlashTestNFC", "onNewIntent");         Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);            //TODO 获取数据进行下一步处理            Log.i("FlashTestNFC--Tag", bytesToHex(tag.getId()));     }        @Override     protected void onResume() {         super.onResume();         Log.i("FlashTestNFC", "onResume");         if (mNfcAdapter != null) {             //添加intent-filter             IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);             IntentFilter tag = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);             IntentFilter tech = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED);             IntentFilter[] filters = new IntentFilter[]{ndef, tag, tech};                //添加 ACTION_TECH_DISCOVERED 情况下所能读取的NFC格式,这里列的比较全,实际我这里是没有用到的,因为测试的卡是NDEF的             String[][] techList = new String[][]{                     new String[]{                             "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"                     }             };             mNfcAdapter.enableForegroundDispatch(this, pIntent, filters, techList);         }     }        @Override     protected void onPause() {         super.onPause();         Log.i("FlashTestNFC", "onPause");         if (mNfcAdapter != null) {             mNfcAdapter.disableForegroundDispatch(this);         }     }        /**      * 2进制to 16进制      * @param src      * @return      */     private static String bytesToHex(byte[] src){         StringBuffer sb = new StringBuffer();         if (src == null || src.length <= 0) {             return null;         }         String sTemp;         for (int i = 0; i < src.length; i++) {             sTemp = Integer.toHexString(0xFF & src[i]);             if (sTemp.length() < 2){                 sb.append(0);             }             sb.append(sTemp.toUpperCase());         }         return sb.toString();     } }

解析:主要其实就是NfcAdapter.enableForegroundDispatch(),开启前台响应;在onNewIntent中获取系统传递过来的数据,并解析;在前台activity停止时,使用NfcAdapter.disableForegroundDispatch()关闭响应。下图是该activity在设置启动模式为singleTop或singleTask情况下,刷卡后该activity生命周期变化:

enableForegroundDispatch源码注释解析,这里大致翻译一下:

  • 将发现的tag(可以理解为NFC刷卡事件)优先分配给应用程序的前台activity;
  • 如果给该方法提供了任何IntentFilters,那么会优先去匹配ACTION_NDEF_DISCOVERED和ACTION_TAG_DISCOVERED。由于ACTION_TECH_DISCOVERED依赖于 IntentFilter 匹配之外的元数据,使用改IntentFilter要通过单独传入techLists来处理的。techLists中的每个第一级条目下的配置必须全部匹配才行。如果任何一级下的内容都匹配,则分派将通过给定的 PendingIntent 进行路由。(这三句话我解释一下:techLists参数是一个二维数组,可以设置很多级,每一级下是第二级,在第二级中放置相关匹配项;看我方法②中对techLists数组的构建方式就能明白)。换句话说,第一级内容是逻辑或关系,第二级内容是逻辑与关系。
  • 如果IntentFilters和techLists都传了null,那么会默认匹配ACTION_TAG_DISCOVERED
  • 这个方法必须在主线程调用,并且activity必须处于前台的情况下。同时,在activity调用enableForegroundDispatch方法后,必须在onPause时调用disableForegroundDispatch进行关闭。
  • Manifest文件中要声明NFC权限。

总结:大概19年8-9月份的时候,那会儿刚开始实习不久,当时手头负责的项目就涉及到NFC刷卡,使用了方案①中的方式。在开发过程中,调试机为自己的华为Mate 20手机,每一次我打开刷卡页面进行刷卡时,都会默认跳转到微信的NFC事件响应页面,这叫一个头大;后来直接找到微信NFC开关,将其关闭后才不影响调试。好在线上手持机设备都是不让用户安装其他应用的。当时还很奇怪,微信到底咋就能强占这NFC响应,现在我终于找到了答案并进行了一定深度的挖掘。
对于这两种方案,我更加偏向于方案②,因为交互上能够体验更好,使用方案①用户可能还会有一个选择的过程。

标签:src,NFC,读取,tech,nfc,Android,intent,android
来源: https://www.cnblogs.com/jiftle/p/16508358.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有