本文首發於 vivo互聯網技術 微信公衆號
連接: https://mp.weixin.qq.com/s/EHomjBy4Tvm8u962J6ZgsA
做者:Sun Daxiang
Google 從 Android 6.0開始,提供了開放的指紋識別相關 API,經過此篇文章能夠幫助開發者接入指紋驗證的基礎功能,而且提供了系統應用基於指紋驗證的功能擴展,如指紋驗證登陸功能核心流程圖和關鍵代碼分析。android
從Android 6.0開始,Android 系統支持指紋識別功能,指紋識別的API主要是FingerprintManager。json
FingerprintManager提供的公共方法有,判斷系統是否支持指紋,系統是否錄入過指紋,發起指紋驗證,取消驗證,驗證結果回調。服務器
隱藏方法有,獲取系統中指紋列表,獲取指紋id等信息。然而 Android 9.0之後 Google官方不推薦使用FingerprintManager 接口, 推薦使用微信
BiometricPrompt代替, 由於BiometricPrompt接口不可以自定義彈框樣式,各業務線還未統一使用,下面會介紹此接口接入方法:app
因指紋功能有Android 6.0和 Android 9.0適配問題,因此使用FingerprintVersionM和FingerprintVersionP分別實現對不一樣 Android 版本的封裝實現。ide
FingerprintManagerWrapper, FIngerpintVersionM,FingerprintVersionP都實現IFingerprintInterface接口,統一經過startAuth() cancelAuth方法。ui
調起指紋驗證和取消指紋驗證加密
核心類說明:spa
核心類的關係圖:線程
經過下面的6步接入後,能夠正常使用指紋驗證功能,包括系統是否支持指紋,是否錄入過指紋,拉起指紋驗證,取消指紋驗證,指紋驗證結果回調,適配Android 6.0和Android 9.0。
第一步:在manifest文件中增長以下指紋權限功能 ,詳細權限見2.1章節
第二步:組合判斷,當前系統版本是否支持指紋功能,而且rom中已經錄入過指紋 ,判斷方法見2.5 和 2.6章節
第三步:使用FingerprintManagerWrapper對象調用IFingerprintInterface統一接口中startAuth方法,拉起指紋驗證功能 方法見2.2章節
第四步:FingerprintVersionM和FingerprintVersionP分別實現IFingerprintInterface接口,分別實現Android 6.0和Android 9.0調起指紋驗證功能
第五步:分別在FingerprintVersionM和FingerprintVersionP註冊指紋驗證成功的回調接口AuthenticationCallback 實現代碼見 2.3章節
第六步:在activity生命週期onStop()調用取消驗證接口,關閉指紋驗證功能 見2.4章節
<!--android 9.0及以上系統使用指紋權限 --> <uses-permission android:name="android.permission.USE_BIOMETRIC"/> <!--android 6.0至8.0及以上系統使用指紋權限 --> <uses-permission android:name="android.permission.USE_FINGERPRINT"/> <!--android 6.0及以上,獲取指紋信息權限,系統應用提高篇中會使用到 --> <uses-permission android:name="android.permission.MANAGE_FINGERPRINT"/>
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中實現Android6.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中實現Android9.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和指紋列表方法,能夠知足其餘業務需求,好比賬號登陸,支付等功能。
一、獲取到指紋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和指紋列表後能夠實現指紋登陸功能
指紋登陸效果
指紋登陸交互時序圖
更多內容敬請關注 vivo 互聯網技術 微信公衆號
注:轉載文章請先與微信號:Labs2020 聯繫。