本文首發於 vivo互聯網技術 微信公衆號
連接:mp.weixin.qq.com/s/EHomjBy4T…
做者:Sun Daxiangandroid
Google 從 Android 6.0開始,提供了開放的指紋識別相關 API,經過此篇文章能夠幫助開發者接入指紋驗證的基礎功能,而且提供了系統應用基於指紋驗證的功能擴展,如指紋驗證登陸功能核心流程圖和關鍵代碼分析。json
從Android 6.0開始,Android 系統支持指紋識別功能,指紋識別的API主要是FingerprintManager。bash
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中實現 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和指紋列表方法,能夠知足其餘業務需求,好比賬號登陸,支付等功能。
一、獲取到指紋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 聯繫。