Android在6.0的时候提供了官方的指纹模块API,最近在使用的时候发现以前的FingerprintManager已经被标识为过时了。
Android 8.0 整合了一套生物识别模块,指纹识别使用BiometricPrompt来调用。
官方文档:FingerprintManager
官方文档:BiometricPrompt
写代码用过时API当然没排面啦,于是我转用了BiometricPrompt,但是也有一个问题,这个API是Android 28才能使用的,也就是说6.0,7.0的手机无法使用,7.0目前还是有一些使用率的,为了能够让所有支持指纹的Android手机都可以使用我两种方法都写了,使用策略模式封装了一下,直接看代码吧,使用Kotlin编写。
关于策略模式的使用可以看我之前的这篇博客
策略模式
interface FingerPrintStrategy {
fun authenticate()//验证指纹
fun cancelAuthenticate()//取消验证
}
interface FingerprintResultCallback {
fun onSucceeded(result:String)
fun onFailed()
fun onError(errorCode:Int,errorMessage:String)
}
@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()
}
}
@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,记得在生命周期结束的时候释放掉,或者使用弱引用以防产生内存泄漏。