本文首發于 vivo互聯網技術 微信公眾號
鏈接: https://mp.weixin.qq.com/s/EHomjBy4Tvm8u962J6ZgsA
作者:Sun Daxiang
Google 從 Android 6.0開始,提供了開放的指紋識別相關 API,通過此篇文章可以幫助開發者接入指紋驗證的基礎功能,并且提供了系統應用基于指紋驗證的功能擴展,如指紋驗證登錄功能核心流程圖和關鍵代碼分析。
從Android 6.0開始,Android 系統支持指紋識別功能,指紋識別的API主要是FingerprintManager。
FingerprintManager提供的公共方法有,判斷系統是否支持指紋,系統是否錄入過指紋,發起指紋驗證,取消驗證,驗證結果回調。
隱藏方法有,獲取系統中指紋列表,獲取指紋id等信息。然而 Android 9.0以后 Google官方不推薦使用FingerprintManager 接口, 推薦使用
BiometricPrompt代替, 因為BiometricPrompt接口不能夠自定義彈框樣式,各業務線還未統一使用,下面會介紹此接口接入方法:


因指紋功能有Android 6.0和 Android 9.0適配問題,所以使用FingerprintVersionM和FingerprintVersionP分別實現對不同 Android 版本的封裝實現。
FingerprintManagerWrapper, FIngerpintVersionM,FingerprintVersionP都實現IFingerprintInterface接口,統一通過startAuth() cancelAuth方法。
調起指紋驗證和取消指紋驗證
核心類說明:

核心類的關系圖:

通過下面的6步接入后,可以正常使用指紋驗證功能,包括系統是否支持指紋,是否錄入過指紋,拉起指紋驗證,取消指紋驗證,指紋驗證結果回調,適配Android 6.0和Android 9.0。
第一步:在manifest文件中增加如下指紋權限功能 ,判斷方法見方法見實現代碼見 2.3章節
第六步:在activity生命周期onStop()調用取消驗證接口,關閉指紋驗證功能 見2.4章節

FingerprintVersionP和FingerprintVersionM分別兼容 Android 6.0和 Android9.0指紋驗證功能,FingerprintManagerWrapper,FingerprintVersionP,FingerprintVersionM統一實現IFingerprintInterface接口
public FingerprintManagerWrapper() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
mFingerprintImp = new FingerprintVersionP();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mFingerprintImp = new FingerprintVersionM();
}
}
}
/**
* 先判斷系統是否支持指紋,系統是否錄入過指紋,再調用指紋認證統一方法
*/
public void startAuth(FingerprintManagerWrapper.FingerVerifyResultListener listener) {
Log.i(TAG, "------startFingerAuthenticate() enter --------");
//判斷指紋設備當前是否可以使用
if (!isHardwareDetected()) {
Log.e(TAG, "------hardware detected!!!--------");
return;
}
//判斷當前是否有指紋
if (!hasEnrolledFingerprints()) {
Log.e(TAG, "-----has no Fingerprints!!!--------");
return;
}
mFingerprintImp.startAuth(listener);
}
/**
* android6.0android9.0統一驗證和取消接口
*/
interface IFingerprintInterface {
public void startAuth(FingerprintManagerWrapper.FingerVerifyResultListener listener);
public void canceAuth();
}
FingerprintVersionM中實現 Android 6.0指紋驗證代碼如下:
public class FingerprintVersionM implements IFingerprintInterface {
@Override
public void startAuth(FingerprintManagerWrapper.FingerVerifyResultListener listener) {
//取消指紋驗證類
CancellationSignal mCancellationSignal = new CancellationSignal();
MyAuthenticationCallback authenticationCallback = new MyAuthenticationCallback();
/**
* 參數說明:
* CryptoObject - 如果需要的話可以添加加密對象CryptoObject
* CancellationSignal - 用來取消指紋驗證,如果想手動取消驗證,調用該參數的cancel方法
* int - 沒意義,默認傳0
* AuthenticationCallback - 回調驗證的結果,成功、失敗等
* Handler - 傳null則默認創建一個在主線程上的Handler來傳遞消息
*/
mFingerprintManager.authenticate(null, mCancellationSignal, 0, authenticationCallback, null); //調用識別接口
}
}
FingerprintVersionP中實現 Android 9.0指紋驗證代碼如下:
public class FingerprintVersionP implements IFingerprintInterface {
@Override
public void startAuth(FingerprintManagerWrapper.FingerVerifyResultListener listener) {
//用來取消指紋驗證
CancellationSignal mCancellationSignal = new CancellationSignal();
//回調驗證的結果,成功、失敗等
AuthenticationCallback mAuthenticationCallback = new BiometricPrompt.AuthenticationCallback(){}
//開始驗證
mBiometricPrompt.authenticate(mCancellationSignal, mContext.getMainExecutor(), mAuthenticationCallback);
}
}
// 指紋驗證失敗回調方法 onAuthenticationFailed() //指紋驗證成功回調方法 , 有系統權限可以通過AuthenticationResult 獲取指紋的信息,指紋名稱,指紋id等 onAuthenticationSucceeded(AuthenticationResult result) //指紋驗證失敗回調,helpMsgId=1006,helpString=6 手指移除太快 onAuthenticationHelp(int helpMsgId, CharSequence helpString) //指紋嘗試多次后,指紋主動關閉。errMsgId=5,errString=指紋操作已取消。 onAuthenticationError(int errMsgId, CharSequence errString)
// android9.0以下使用 android.hardware.fingerprint.FingerprintManager.AuthenticationCallback
//android 9.0以上使用 android.hardware.biometrics.BiometricPrompt.AuthenticationCallback
private class MyAuthenticationCallback extends AuthenticationCallback {
@Override
public void onAuthenticationFailed() {
//指紋驗證失敗回調方法
}
@Override
public void onAuthenticationSucceeded(AuthenticationResult result) {
//指紋驗證成功回調方法 , 有系統權限可以通過AuthenticationResult 獲取指紋的信息,指紋名稱,指紋id等
}
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
//指紋驗證失敗回調,helpMsgId=1006,helpString=6 手指移除太快
}
@Override
public void onAuthenticationError(int errMsgId, CharSequence errString) {
//指紋嘗試多次后,指紋主動關閉。errMsgId=5,errString=指紋操作已取消。
}
}
在activity的onStop方法中取消指紋驗證功能
// 屏下指紋彈框home鍵或者被其他頁面全部覆蓋后,需要關閉,重新拉起
@Override
protected void onStop() {
super.onStop();
//上面調起指紋驗證方法中,傳入的CancellationSignal對象
mCancellationSignal.cancel();
}
FingerprintManager雖然在 Android 9.0以后不推薦使用,但是Google各系統版本都支持FingerprintManager,而且9.0及以上版本暫未提供判斷是否支持指紋功能的 API
/**
* 判斷當前指紋功能是否可用
*
* @return
*/
public boolean isHardwareDetected() {
boolean isHardwareSupport = false;
try {
if (mFingerprintManager != null) {
isHardwareSupport = mFingerprintManager.isHardwareDetected();
}
} catch (Exception e) {
Log.e(TAG, "isHardwareDetected err ", e);
}
Log.e(TAG, "isHardwareDetected(), isHardwareSupport= " + isHardwareSupport);
return isHardwareSupport;
}
同上使用FingerprintManager判斷系統是否錄入過指紋
/**
* 判斷是否錄入過指紋
*
* @return
*/
public boolean hasEnrolledFingerprints() {
boolean hasEnrolledFinger = false;
try {
if (mFingerprintManager != null) {
hasEnrolledFinger = mFingerprintManager.hasEnrolledFingerprints();
}
} catch (Exception e) {
Log.e(TAG, "hasEnrolledFingerprints err ", e);
}
Log.e(TAG, "hasEnrolledFingerprints(), hasEnrolledFinger= " + hasEnrolledFinger);
return hasEnrolledFinger;
}
以上基礎篇介紹了 Google提供的公共 API , 基本滿足指紋驗證的功能。
下面介紹獲取指紋id和指紋列表方法,可以滿足其他業務需求,比如帳號登錄,支付等功能。
1、獲取到指紋id
指紋驗證成功后,根據FingerprintManager.AuthenticationResult 對象可以通過反射方法獲取到指紋id(指紋id是隱藏屬性),需要添加權限
android.permission.MANAGE_FINGERPRINT, Android 6.0和 Android9.0中Fingerprint對象有差異:
Android 6.0中Fingerprint中包含有指紋id的屬性mFingerprintId和public方法getFingerprintId
Android 9.0 中Fingerprint類繼承BiometricAuthenticator.Identifer,并且指紋id也放入此類中,屬性名為mBiometricId ,方法為getBiometricId

(Android 9.0 相關類依賴關系)

(Android6.0類圖依賴關系)
因此使用反射獲取Fingerprint對象和指紋id方法需要適配 Android 6.0和 Android 9.0,詳細方法如下:
Android 6.0中AuthenticationResult反射獲取Fingerprint對象,Fingerprint對象getFingerId獲取到指紋id
Android 9.0以上指紋信息放在Fingerprint的父類中,所以需要通過clzz.getSuperclass()獲取父類對象,在反射方法getBiometricId獲取指紋 id
private static int getFingerId(AuthenticationResult result) {
int fingerId = -1;
try {
Field field = result.getClass().getDeclaredField("mFingerprint");
field.setAccessible(true);
Object fingerPrint = field.get(result);
Class<?> clzz = Class.forName("android.hardware.fingerprint.Fingerprint");
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
Log.i(TAG, "-------ANDROID Q-------");
Class<?> supClass = clzz.getSuperclass();
Method getBiometricId = supClass.getDeclaredMethod("getBiometricId");
fingerId = (int) getBiometricId.invoke(fingerPrint);
} else {
Log.i(TAG, "------- ANDROID M-P-------");
Method getFingerId = clzz.getDeclaredMethod("getFingerId");
fingerId = (int) getFingerId.invoke(fingerPrint);
}
Log.d(TAG, "fingerId=" + fingerId);
} catch (Exception e) {
Log.e(TAG, "", e);
}
return fingerId;
}
系統錄入指紋,可以通過反射方法獲取到指紋列表中,各指紋信息

通過反射后獲取指紋列表json字符串:
android6.0:[{"mDeviceId":0,"mFingerId":1147763748,"mGroupId":0,"mName":"指紋 1"},{"mDeviceId":0,"mFingerId":412764029,"mGroupId":0,"mName":"指紋 2"}]
android9.0: [{"mGroupId":0,"mBiometricId":-714471355,"mDeviceId":517254275456,"mName":"指紋 1"},{"mGroupId":0,"mBiometricId":-803114291,"mDeviceId":517254275456,"mName":"指紋 2"}]
詳細的反射代碼如下:
1: 將反射獲取的指紋列表json字符串,使用gson轉為AccountFingerprint對象(兼容android6.0-android10.0)
Gson gson = new Gson();
Object object = getEnrolledFingerprints(mFingerprintManager);
String fingerListString = gson.toJson(object)
// android6.0-android9.0 [{"mDeviceId":0,"mFingerId":1147763748,"mGroupId":0,"mName":"指紋 1"},{"mDeviceId":0,"mFingerId":412764029,"mGroupId":0,"mName":"指紋 2"}]
// android10.0 [{"mGroupId":0,"mBiometricId":-714471355,"mDeviceId":517254275456,"mName":"指紋 1"},{"mGroupId":0,"mBiometricId":-803114291,"mDeviceId":517254275456,"mName":"指紋 2"}]
List<AccountFingerprint> list = gson.fromJson(fingerListString, new TypeToken<List<AccountFingerprint>>() {}.getType());
2:反射FingerprintManager調用getEnrolledFingerprints方法獲取已錄入指紋列表
/**
* 反射獲取當前用戶的所有指紋信息列表
*
* @param fm FingerprintManagerWrapper
* @return 指紋信息列表
*/
public static Object getEnrolledFingerprints(FingerprintManager fm) {
try {
if (fm != null) {
Object obj = invokeMethod(fm, "getEnrolledFingerprints");
return obj;
}
} catch (Exception e) {
VLog.e(TAG, "getEnrolledFingerprints()", e);
}
return null;
}
3: 自定義的AccountFingerprint bean,兼容android6-android10
//創建的指紋bean
public class AccountFingerprint {
@SerializedName("mBiometricId")
private int mBiometricId;
@SerializedName("mFingerId")
private int mFingerId;
@SerializedName("mGroupId")
private int mGroupId;
@SerializedName("mDeviceId")
private long mDeviceId;
@SerializedName("mName")
private String mPrintName;
}
舉個栗子:獲取指紋id和指紋列表后可以實現指紋登錄功能
指紋登錄效果

指紋登錄交互時序圖

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。