您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)Android開發(fā)如何實(shí)現(xiàn)NFC刷卡讀取,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
①:Manifest注冊(cè)方式:這種方式主要是在Manifest文件對(duì)應(yīng)的activity下,配置過濾器,以響應(yīng)不同類型NFC Action。使用這種方式,在刷卡時(shí),如果手機(jī)中有多個(gè)應(yīng)用都存在該NFC實(shí)現(xiàn)方案,系統(tǒng)會(huì)彈出能響應(yīng)NFC事件的應(yīng)用列表供用戶選擇,用戶需要點(diǎn)擊目標(biāo)應(yīng)用來響應(yīng)本次NFC刷卡事件。目前我公司這邊項(xiàng)目中使用了該邏輯,比較簡(jiǎn)便,這里先貼一下該方式的實(shí)現(xiàn)邏輯。
Manifest配置:
<!--權(quán)限要加,這是一個(gè)普通權(quán)限,不需要?jiǎng)討B(tài)申請(qǐng),但是在小米手機(jī)里需要?jiǎng)討B(tài)申請(qǐng)--> <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"> <!--透明主題,把刷卡變成一個(gè)無感知的過程--> <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" /> <!--使用這個(gè)過濾器 這里其實(shí)還要用 meta-data 配置一下標(biāo)簽過濾,--> <!--我項(xiàng)目中是 NDEF_DISCOVERED 這個(gè)TECH_DISCOVERED形同虛設(shè)--> </intent-filter> <meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech" /> </activity> </application>
nfc_tech.xml:這個(gè)文件就是TECH_DISCOVERED需要配置的,其中,tech-list之間是邏輯或關(guān)系,tech之間是邏輯與關(guān)系,與方案②中的techLists原理以及用途是類似的。
<?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:
public class NfcActivity extends Activity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_nfc); initData(); } /** * 初始化數(shù)據(jù) */ 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); // 根據(jù)包名打開對(duì)應(yīng)的設(shè)置界面 startActivity(intent); } //我項(xiàng)目中是拿了NFC卡的tag中的id數(shù)據(jù),這根據(jù)具體情況來; // 可以在NfcAdapter源碼中查看,具體能拿到哪些數(shù)據(jù) Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG); String id = bytesToHex(tag.getId()); //TODO 目前我這邊項(xiàng)目中,拿到數(shù)據(jù)后,通過EventBus分發(fā)到對(duì)應(yīng)的activity,當(dāng)然也能使用其他分發(fā)響應(yīng)方式, //關(guān)閉動(dòng)畫,畢竟對(duì)用戶來說,刷卡應(yīng)當(dāng)是一個(gè)無感知的過程 overridePendingTransition(0, 0); finish(); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); initData(); } /** * 2轉(zhuǎn)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轉(zhuǎn)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(); } }
②:前臺(tái)響應(yīng)機(jī)制:這種方式與第一種的區(qū)別如下:方法一中,NFC事件由系統(tǒng)分發(fā),需要選擇應(yīng)用去響應(yīng)事件;而方法二,直接使用前臺(tái)activity來捕獲NFC事件進(jìn)行響應(yīng),并且優(yōu)先級(jí)高于方案一。
下面對(duì)該方案進(jìn)行解析,直接懟上代碼。這里我新建了一個(gè)NfcTestActivity進(jìn)行測(cè)試,布局文件就補(bǔ)貼了,隨便丟一個(gè)就行。
NfcTestActivity:
/** * @author Flash * 創(chuàng)建時(shí)間: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里或者這里設(shè)置當(dāng)前activity啟動(dòng)模式,否則每次向陽(yáng)NFC事件,activity會(huì)重復(fù)創(chuàng)建 // 當(dāng)然也要按照具體情況來,你設(shè)置成singleTask也不是不行, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); //這里必須setIntent,set NFC事件響應(yīng)后的intent才能拿到數(shù)據(jù) setIntent(intent); Log.i("FlashTestNFC", "onNewIntent"); Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG); //TODO 獲取數(shù)據(jù)進(jìn)行下一步處理 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格式,這里列的比較全,實(shí)際我這里是沒有用到的,因?yàn)闇y(cè)試的卡是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進(jìn)制to 16進(jìn)制 * @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(); } }
解析:主要其實(shí)就是NfcAdapter.enableForegroundDispatch(),開啟前臺(tái)響應(yīng);在onNewIntent中獲取系統(tǒng)傳遞過來的數(shù)據(jù),并解析;在前臺(tái)activity停止時(shí),使用NfcAdapter.disableForegroundDispatch()關(guān)閉響應(yīng)。下圖是該activity在設(shè)置啟動(dòng)模式為singleTop或singleTask情況下,刷卡后該activity生命周期變化:
enableForegroundDispatch源碼注釋解析,這里大致翻譯一下:
將發(fā)現(xiàn)的tag(可以理解為NFC刷卡事件)優(yōu)先分配給應(yīng)用程序的前臺(tái)activity;
如果給該方法提供了任何IntentFilters,那么會(huì)優(yōu)先去匹配ACTION_NDEF_DISCOVERED和ACTION_TAG_DISCOVERED。由于ACTION_TECH_DISCOVERED依賴于 IntentFilter 匹配之外的元數(shù)據(jù),使用改IntentFilter要通過單獨(dú)傳入techLists來處理的。techLists中的每個(gè)第一級(jí)條目下的配置必須全部匹配才行。如果任何一級(jí)下的內(nèi)容都匹配,則分派將通過給定的 PendingIntent 進(jìn)行路由。(這三句話我解釋一下:techLists參數(shù)是一個(gè)二維數(shù)組,可以設(shè)置很多級(jí),每一級(jí)下是第二級(jí),在第二級(jí)中放置相關(guān)匹配項(xiàng);看我方法②中對(duì)techLists數(shù)組的構(gòu)建方式就能明白)。換句話說,第一級(jí)內(nèi)容是邏輯或關(guān)系,第二級(jí)內(nèi)容是邏輯與關(guān)系。
如果IntentFilters和techLists都傳了null,那么會(huì)默認(rèn)匹配ACTION_TAG_DISCOVERED
這個(gè)方法必須在主線程調(diào)用,并且activity必須處于前臺(tái)的情況下。同時(shí),在activity調(diào)用enableForegroundDispatch方法后,必須在onPause時(shí)調(diào)用disableForegroundDispatch進(jìn)行關(guān)閉。
Manifest文件中要聲明NFC權(quán)限。
關(guān)于“Android開發(fā)如何實(shí)現(xiàn)NFC刷卡讀取”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。