Android在6.0的時候提供了官方的指紋模塊API,最近在使用的時候發現以前的FingerprintManager已經被標識爲過時了。
Android 8.0 整合了一套生物識別模塊,指紋識別使用BiometricPrompt來調用。
官方文檔:FingerprintManager
寫代碼用過時API當然沒排面啦,於是我轉用了BiometricPrompt,但是也有一個問題,這個API是Android 28才能使用的,也就是說6.0,7.0的手機無法使用,7.0目前還是有一些使用率的,爲了能夠讓所有支持指紋的Android手機都可以使用我兩種方法都寫了,使用策略模式封裝了一下,直接看代碼吧,使用Kotlin編寫。
使用策略模式
關於策略模式的使用可以看我之前的這篇博客
策略模式
定義接口FingerPrintStrategy
interface FingerPrintStrategy {
fun authenticate()//驗證指紋
fun cancelAuthenticate()//取消驗證
}
定義回調接口
interface FingerprintResultCallback {
fun onSucceeded(result:String)
fun onFailed()
fun onError(errorCode:Int,errorMessage:String)
}
FingerprintManager策略具體實現
@TargetApi(23)
class FingerprintStrategyForM(context: Context, callback: FingerprintResultCallback) : FingerPrintStrategy{
private val TAG = "FingerprintStrategyForM"
private val mContext = context
private var mCancellationSignal: CancellationSignal? = null
private var mFingerprintManager: FingerprintManager? = null
private var mCipher: Cipher? = null
private var mCallback:FingerprintResultCallback = callback
init {
mFingerprintManager = mContext.getSystemService(Context.FINGERPRINT_SERVICE) as FingerprintManager
mCancellationSignal = CancellationSignal()
mCancellationSignal!!.setOnCancelListener {
PsyLog.d(TAG,"Canceled!!!")
}
}
/**
* 判斷手機是否有指紋模塊
*/
private fun hasFingerHardware():Boolean{
if (mFingerprintManager == null){
return false
}
return mFingerprintManager!!.isHardwareDetected
}
/**
* 判斷用戶是否給手機錄入過指紋
*/
private fun hasFingerInput():Boolean{
if (mFingerprintManager == null){
return false
}
return mFingerprintManager!!.hasEnrolledFingerprints()
}
/**
* 指紋校驗具體實現
*/
override fun authenticate() {
if (!hasFingerHardware()){
mCallback.onError(BIOMETRIC_ERROR_HW_NOT_PRESENT,mContext.getString(R.string.phone_no_biometric_sensor))
return
}
if (!hasFingerInput()){
mCallback.onError(BIOMETRIC_ERROR_NO_BIOMETRICS,mContext.getString(R.string.phone_not_input_fingerprint))
return
}
mFingerprintManager?.authenticate(null,mCancellationSignal,0,object:FingerprintManager.AuthenticationCallback(){
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
super.onAuthenticationError(errorCode, errString)
mCallback.onError(errorCode,errString.toString())
}
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult?) {
super.onAuthenticationSucceeded(result)
mCallback.onSucceeded(result.toString())
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
mCallback.onFailed()
}
},null)
}
/**
* 取消校驗
*/
override fun cancelAuthenticate() {
mCancellationSignal?.cancel()
}
}
BiometricPrompt策略具體實現
@TargetApi(28)
class FingerprintStrategyForP(context:Context, callback:FingerprintResultCallback):FingerPrintStrategy {
private val TAG = "FingerprintStrategyForP"
private val mContext:Context = context
private var mCancellationSignal: CancellationSignal? = null
private var mBiometricPrompt: BiometricPrompt? = null
private var mAuthenticationCallback: BiometricPrompt.AuthenticationCallback? = null
private val mCallback = callback
init {
//setNegativeButton在這裏是必須的,不寫會報錯,校驗指紋是的UI必須有取消響應,此時按back鍵沒用的
mBiometricPrompt = BiometricPrompt.Builder(mContext)
.setTitle(mContext.getString(R.string.authentication))
.setDescription(mContext.getString(R.string.please_press_the_fingerprint_sensing_area_to_verify_the_fingerprint))
.setNegativeButton(mContext.getString(R.string.cancel), getMainExecutor(mContext),
DialogInterface.OnClickListener { dialog, which ->
PsyLog.d(TAG, "Cancel button clicked")
cancelAuthenticate()
})
.build()
mCancellationSignal = CancellationSignal()
mCancellationSignal!!.setOnCancelListener {
PsyLog.d(TAG,"Canceled!!!")
}
mAuthenticationCallback = object : BiometricPrompt.AuthenticationCallback(){
//api版本判斷,是否有指紋傳感器等BiometricPrompt會自己判斷,在這裏返回
override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
super.onAuthenticationError(errorCode, errString)
mCallback.onError(errorCode,errString.toString())
}
//校驗成功
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
mCallback.onFailed()
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult?) {
super.onAuthenticationSucceeded(result)
mCallback.onSucceeded(result.toString())
}
}
}
/**
* 指紋校驗具體實現
*/
override fun authenticate(){
mBiometricPrompt!!.authenticate(mCancellationSignal!!, getMainExecutor(mContext), mAuthenticationCallback!!)
}
/**
* 取消校驗具體實現
*/
override fun cancelAuthenticate(){
mCancellationSignal?.cancel()
}
}
定義異常碼
BiometricPrompt源碼中就有很多異常碼(官方文檔中異常碼定義)
爲了返回值統一我們可以給FingerprintManager也定義一樣的異常碼。
const val BIOMETRIC_ERROR_HW_NOT_PRESENT = 12
const val BIOMETRIC_ERROR_HW_UNAVAILABLE = 1
const val BIOMETRIC_ERROR_LOCKOUT = 7
const val BIOMETRIC_ERROR_LOCKOUT_PERMANENT = 9
const val BIOMETRIC_ERROR_NO_BIOMETRICS = 11
const val BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14
const val BIOMETRIC_ERROR_NO_SPACE = 4
const val BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15
const val BIOMETRIC_ERROR_TIMEOUT = 3
具體操作類
我的建的工程minSDK設置就是23,所以這裏不用再判斷版本號是否小於Android M了。如果你的minSDK比23小,需要再這裏加一個判斷,小於23直接返回。
class FingerPrintHelper(context: Context,fingerprintResultCallback: FingerprintResultCallback ) {
//api 28 以下使用FingerprintManager策略,以上用BiometricPrompt策略
private var strategy:FingerPrintStrategy = if (Build.VERSION.SDK_INT<Build.VERSION_CODES.P){
FingerprintStrategyForM(context,fingerprintResultCallback)
}else{
FingerprintStrategyForP(context,fingerprintResultCallback)
}
fun authenticate(){
strategy.authenticate()
}
fun cancelAuthenticate(){
strategy.cancelAuthenticate()
}
}
使用時使用如下代碼就行了,
FingerPrintHelper(mContext,
object : FingerprintResultCallback {
override fun onSucceeded(result: String) {
PsyLog.d(TAG, "onSucceeded")
//校驗成功,跳轉頁面
}
override fun onFailed() {
PsyLog.d(TAG, "onFailed")
//每次指紋識別沒通過都會走到這裏
}
override fun onError(errorCode: Int, errorMessage: String) {
PsyLog.d(TAG, "onError")
showMessage(errorMessage)
//這裏時一些錯誤情況,比如沒有指紋傳感器,錯誤太多指紋被鎖等
}
}).authenticate()
FingerPrintHelper引用了context,記得在生命週期結束的時候釋放掉,或者使用弱引用以防產生內存泄漏。