場景
APP中讀取NFC卡中的標簽ID,作為用戶的唯一標識進行登錄驗證。
首先需要確保手機支持NFC功能。其次具備一張NFC卡片。
讀取id就是利用的讀卡器模式,當把卡片靠近手機的NFC天線的時候,NFC會識別到卡,
然后把卡對象裝到intent里面,
並發送廣播NfcAdapter.ACTION_TECH_DISCOVERED,
應用程序接到這個廣播之后,通過intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)來獲取到卡對象,
然后就可以對卡進行讀寫
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
關注公眾號
霸道的程序猿
獲取編程相關電子書、教程推送與免費下載。
實現
1、新建項目,添加權限
打開Android Studio新建一個項目,在AndroidManifest.xml中添加權限。
<!-- NFC所需權限--> <uses-permission android:name="android.permission.NFC" /> <!-- 要求當前設備必須要有NFC芯片 --> <uses-feature android:name="android.hardware.nfc" android:required="true" />
2、將要讀取NFC的Activity設置為singleTop
這里是在MainActivity中
<activity android:name=".MainActivity" android:launchMode="singleTop"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
Activity共有四種啟動模式:
standard
標准模式,也是Activity的默認啟動模式,允許存在多個Activity實例,
每次啟動頁面時都會生成一個新的Activity實例。
singleTop
相比於standard,有新的頁面啟動請求時,當目標Activity處於當前棧頂時,
會調用Activity的onNewIntent()方法,但不創建新實例;其他情況都和standard一致。
其他兩種不做介紹。
NFC檢測到對象時,會在系統startActivity,那么目標activity已經是啟動了,
所以我們需要在onNewIntent方法中接受tag對象,同時activity啟動模式設為singleTop或singleTask也為了避免重復創建實例
3、設計頁面布局
打開activity_main.xml,修改如下
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#151414" > <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_centerInParent="true" > <TextView android:layout_width="wrap_content" android:text="讀取到的卡UID: " android:textColor="#fff" android:layout_height="wrap_content" /> <TextView android:textColor="#fff" android:id="@+id/tv_uid" android:text=" " android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> </RelativeLayout>
4、修改Activity
在OnCreate方法中,獲取NfcAdapter實例,然后獲取通知,判斷支持NFC並且打開后,當獲取通知后會調用onNewIntent方法。
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //獲取顯示文本框 tvUid = (TextView) findViewById(R.id.tv_uid); //獲取NfcAdapter實例 nfcAdapter = NfcAdapter.getDefaultAdapter(this); //獲取通知 pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); //如果獲取不到則不支持NFC if (nfcAdapter == null) { Toast.makeText(MainActivity.this,"設備不支持NFC",Toast.LENGTH_LONG).show(); return; } //如果獲取到的為不可用狀態則未啟用NFC if (nfcAdapter!=null&&!nfcAdapter.isEnabled()) { Toast.makeText(MainActivity.this,"請在系統設置中先啟用NFC功能",Toast.LENGTH_LONG).show(); return; } //因為啟動模式是singleTop,於是會調用onNewIntent方法 onNewIntent(getIntent()); }
在onNewIntent中,解析intent攜帶的卡對象
@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); //獲取、傳遞、解析intent對象,intent中攜帶卡對象 resolveIntent(intent); } //解析intent void resolveIntent(Intent intent) { //獲取intent中攜帶的標簽對象 Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); if (tag != null) { //處理標簽對象 processTag(intent); } }
在處理標簽對象的方法中獲取攜帶的數據中的ID字節數組並轉換成十六進制字符串顯示。
//處理tag public void processTag(Intent intent) { //獲取到卡對象 Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); //獲取卡id這里即uid,字節數組類型 byte[] aa = tagFromIntent.getId(); //字節數組轉十六進制字符串 String str = ByteArrayToHexString(aa); tvUid.setText(str); }
完整Activity代碼
package com.badao.nfcdemo; import androidx.appcompat.app.AppCompatActivity; import android.app.PendingIntent; import android.content.Intent; import android.nfc.NfcAdapter; import android.nfc.Tag; import android.os.Bundle; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private NfcAdapter nfcAdapter; private PendingIntent pendingIntent; private TextView tvUid; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //獲取顯示文本框 tvUid = (TextView) findViewById(R.id.tv_uid); //獲取NfcAdapter實例 nfcAdapter = NfcAdapter.getDefaultAdapter(this); //獲取通知 pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); //如果獲取不到則不支持NFC if (nfcAdapter == null) { Toast.makeText(MainActivity.this,"設備不支持NFC",Toast.LENGTH_LONG).show(); return; } //如果獲取到的為不可用狀態則未啟用NFC if (nfcAdapter!=null&&!nfcAdapter.isEnabled()) { Toast.makeText(MainActivity.this,"請在系統設置中先啟用NFC功能",Toast.LENGTH_LONG).show(); return; } //因為啟動模式是singleTop,於是會調用onNewIntent方法 onNewIntent(getIntent()); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); //獲取、傳遞、解析intent對象,intent中攜帶卡對象 resolveIntent(intent); } //解析intent void resolveIntent(Intent intent) { //獲取intent中攜帶的標簽對象 Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); if (tag != null) { //處理標簽對象 processTag(intent); } } //字節數組轉換十六進制 private String ByteArrayToHexString(byte[] inarray) { int i, j, in; String[] hex = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" }; String out = ""; for (j = 0; j < inarray.length; ++j) { in = (int) inarray[j] & 0xff; i = (in >> 4) & 0x0f; out += hex[i]; i = in & 0x0f; out += hex[i]; } return out; } //處理tag public void processTag(Intent intent) { //獲取到卡對象 Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); //獲取卡id這里即uid,字節數組類型 byte[] aa = tagFromIntent.getId(); //字節數組轉十六進制字符串 String str = ByteArrayToHexString(aa); tvUid.setText(str); } @Override protected void onPause() { super.onPause(); if (nfcAdapter != null) //設置程序不優先處理 nfcAdapter.disableForegroundDispatch(this); } @Override protected void onResume() { super.onResume(); if (nfcAdapter != null) //設置程序優先處理 nfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null); } }
5、運行app,打開nfc,將NFC卡片靠近手機
可以以debug模式運行,依次打斷點查看效果