Android指紋識別 看這一篇就夠了

世上無難事 只怕有心人

Android9.0因爲沒法自定義指紋識別框 因此不額外考慮了 bash

本文的場景僅定位於Android6.0以上app

本文實現的登陸流程ide


流程思路啓發於支付寶 登錄界面判斷是否開啓指紋登陸 進入主界面可設置是否開啓指紋登陸post

基本的流程算是整完了哈 接下來就是去瞟一瞟官方的文檔啦(~o ̄3 ̄)~動畫


下面是Android官網中的文檔 指紋識別類ui

這個方法爲指紋識別中的核心方法 用於拉起指紋識別this

關於每一個參數的解釋 加密

crypto: FingerprintManagerCompat.CryptoObject?   密碼對象的包裝類 
spa

flags: Int   可選標誌 傳0便可code

callback: FingerprintManagerCompat.AuthenticationCallback 驗證結果的回調

cancel: CancellationSignal 取消操做並向取消監聽器發出信號 用於取消指紋識別時

handler: Handler 能夠直接傳NULL

首先咱們建立一個Cipher形式的密碼對象包裝類 建立密匙時 使用AES + CBC + PADDING_PKCS7進行加密

class CipherHelper {
    private var _keystore : KeyStore
    init {
        _keystore = KeyStore.getInstance(KEYSTORE_NAME)
        _keystore.load(null)
    } 
   companion object{
       const val KEY_NAME = "MyCipherHelper" 
       const val KEYSTORE_NAME = "AndroidKeyStore" 
       const val KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
       const val BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC
       const val ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
       const val TRANSFORMATION = "$KEY_ALGORITHM/$BLOCK_MODE/$ENCRYPTION_PADDING"
    }
    fun createCipher(retry : Boolean) : Cipher{
        val key = GetKey()
        val cipher = Cipher.getInstance(TRANSFORMATION)
        try {
            cipher.init(Cipher.ENCRYPT_MODE or Cipher.DECRYPT_MODE,key)
        }catch (e : KeyPermanentlyInvalidatedException){ 
           _keystore.deleteEntry(KEY_NAME) 
           if(retry){
                createCipher(false)
            }else{ 
               Exception("Could not create the cipher for fingerprint authentication.",e)
            } 
       }  
      return cipher
    } 
   private fun GetKey() : Key { 
       if(!_keystore.isKeyEntry(KEY_NAME)){ 
           CreateKey()
        } 
       return _keystore.getKey(KEY_NAME,null)
    } 
   /** * @description 生成密匙 */ 
   private fun CreateKey(){ 
       val keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM, KEYSTORE_NAME)
        val keyGenParameterSpec = KeyGenParameterSpec.Builder(KEY_NAME, 
           KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) 
           .setBlockModes(BLOCK_MODE) 
           .setEncryptionPaddings(ENCRYPTION_PADDING)  
           .setUserAuthenticationRequired(true)
           .build()
        keyGenerator.init(keyGenParameterSpec)
        keyGenerator.generateKey()
    }
}複製代碼

定義一個用於指紋驗證的彈窗

class FingerprintVerifyDialog(context: Activity): Dialog(context, R.style.TransparentDialogStyle) {
    private lateinit var onClickListener : OnClickListener
    private var tipText : TextView
    private var cancelBtn : TextView
    init { 
        setContentView(R.layout.finger_verify_dialog)
        setCanceledOnTouchOutside(false)
        tipText = findViewById(R.id.finger_verify_tip)
        cancelBtn = findViewById(R.id.cancel_finger_verify_btn)
        cancelBtn.setOnClickListener { onClickListener.onCancel()
            this.dismiss()
         } 
       windowAnim() 
   } 
   override fun onBackPressed() {
        super.onBackPressed()
        onClickListener.onDismiss()
    } 
   fun setOnClickListener(onClickListener: OnClickListener) : FingerprintVerifyDialog{
        this.onClickListener = onClickListener
        return this
    }
    fun setTipText(tip : String,color : Int){
        tipText.text = tip 
        tipText.setTextColor(color)
    }
    interface OnClickListener{
        /**  
      * @description 取消驗證
        */
        fun onCancel() 
       /**   
     * @description 關閉彈窗 
       */
        fun onDismiss()
    }
    private fun windowAnim() {
        val window = window!! //獲得對話框 
        window.setWindowAnimations(R.style.dialogWindowAnim) //設置窗口彈出動畫
        window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_PANEL)
        window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
        window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
    }
}複製代碼

定義一個SharePrefences 用於存儲用戶打開指紋登陸的標記

class MyPreference<T>(private val name : String, private val default : T) {
    private val sharedPreferences : SharedPreferences
            by lazy { MyApplication.instance.applicationContext.getSharedPreferences(Constans.SHARE_PREFERENCE_NAME,Context.MODE_PRIVATE)} 
   operator fun getValue(thisRef : Any?,property : KProperty<*>) : T = getSharedPreferences(name,default)
   operator fun setValue(thisRef: Any?,property: KProperty<*>,value: T) = putSharedPreferences(name,value)
    private fun putSharedPreferences(name : String,value : T)  = with(sharedPreferences.edit()){
        when(value){
            is Int -> putInt(name,value)
            is String -> putString(name,value)
            is Float -> putFloat(name,value)
            is Long -> putLong(name,value)
            is Boolean -> putBoolean(name,value)
            else -> throw IllegalArgumentException("SharedPreference can't be save this type")
        }.apply()
    }
    private fun getSharedPreferences(name : String,default: T) : T = with(sharedPreferences){
        val res : Any = when(default){
            is Int -> getInt(name,default)
            is Boolean -> getBoolean(name,default)
            is String -> getString(name,default)!!
            is Float -> getFloat(name,default)
            is Long -> getLong(name,default)
            else -> throw IllegalArgumentException("SharedPreference can't be get this type")
        }
        return res as T
    }
}
object DelegatesExt {
    fun <T> myPreferences(name : String,default : T) = MyPreference(name,default)
}
class Constans{
    companion object{
        const val SHARE_PREFERENCE_NAME = "MySharePreference"
        const val OPEN_FINGERPRINT_LOGIN = "open_fingerprint_login"
    }
}複製代碼

定義一個接口 用於回調監聽

interface FingerprintCallback {
    /** * @description 無指紋硬件或硬件不可用 */
    fun onHwUnavailable()
    /** * @description 未添加指紋 */
    fun onNoneEnrolled() 
   /** * @description 驗證成功 */
    fun onSucceeded()
    /** * @description 驗證失敗 */ 
   fun onFailed()
    /** * @description 取消驗證 */
    fun onCancel()
}複製代碼

定義一個擴展類 提早初始初始化加密類 封裝指紋驗證方法 添加監聽 彈出指紋驗證彈窗 對驗證結果回調處理 還有判斷設備是否支持指紋識別方法

class FingerprintImpl {
    private lateinit var context: Activity
    private lateinit var fingerprintCallback: FingerprintCallback
    lateinit var fingerprintVerifyDialog: FingerprintVerifyDialog
    private lateinit var cancellationSignal : CancellationSignal
    companion object{
        private lateinit var cryptoObject : CryptoObject
        val instance by lazy {
            try {
                cryptoObject = CryptoObject(CipherHelper().createCipher(true))
            }catch (e : Exception){
                e.printStackTrace()
            }
            FingerprintImpl()
 }
    }
    /** * @description 初始化並調用指紋驗證 */
    fun authenticate(context : Activity,callback: FingerprintCallback){
        this.context = context
        this.fingerprintCallback = callback
        cancellationSignal = CancellationSignal()
        fingerprintVerifyDialog = FingerprintVerifyDialog(context).setOnClickListener(onClickListener)
        cancellationSignal.setOnCancelListener { fingerprintVerifyDialog.dismiss() }
        from(context).authenticate(cryptoObject,0,cancellationSignal,authenticationCallback,null)
        fingerprintVerifyDialog.show()
    }
    /** * @description 驗證框監聽 */
    private val onClickListener : OnClickListener = object : OnClickListener{
        override fun onCancel() {
            fingerprintCallback.onCancel() 
       }
        override fun onDismiss() {
            cancellationSignal.cancel() 
       }
    }
    /** * @description 驗證結果回調 */
    private val authenticationCallback = object  : AuthenticationCallback(){
        override fun onAuthenticationError(errMsgId: Int, errString: CharSequence?) { 
           super.onAuthenticationError(errMsgId, errString)
            if(errMsgId != 5) fingerprintVerifyDialog.setTipText(errString!!.toString(),Color.RED)
        }
        override fun onAuthenticationSucceeded(result: AuthenticationResult?) {
            super.onAuthenticationSucceeded(result)
            fingerprintVerifyDialog.setTipText("驗證成功",Color.GREEN)
            fingerprintVerifyDialog.dismiss()
            fingerprintCallback. ()
        }
        override fun onAuthenticationHelp(helpMsgId: Int, helpString: CharSequence?) {
            super.onAuthenticationHelp(helpMsgId, helpString)
            fingerprintVerifyDialog.setTipText(helpString!!.toString(),Color.YELLOW)
        } 
       override fun onAuthenticationFailed() {
            super.onAuthenticationFailed()
            fingerprintVerifyDialog.setTipText("驗證失敗",Color.RED)
            fingerprintCallback.onFailed()
        }
    }
    /** * @description 判斷當前是否支持指紋識別 */ 
   fun canAuthenticate(context: Context,fingerprintCallback: FingerprintCallback) : Boolean{
        /** * @description 是否支持指紋識別 */
        if(!from(context).isHardwareDetected){
            fingerprintCallback.onHwUnavailable() 
           return false
        } 
       /** * @description 是否已經添加指紋 */ 
       if(!from(context).hasEnrolledFingerprints()){ 
           fingerprintCallback.onNoneEnrolled()
            return false
        }
        return true
    }
}複製代碼

最後定義一個管理類 提供了指紋識別給外部調用 以及機器是否支持指紋識別的字段

class FingerprintManager(private var context: Activity, private var callback: FingerprintCallback) {
    //是否支持指紋識別
    var isSupportFingerLogin : Boolean = false
    private var fingerprintImpl : FingerprintImpl = FingerprintImpl.instance
    init {
        isSupportFingerLogin = fingerprintImpl.canAuthenticate(context,callback)
    }
   /**
    * @description 開始指紋識別
    */ 
   fun autherticate(){ 
       fingerprintImpl.authenticate(context,callback)
    }
}複製代碼

到這裏 能夠說 大功告成啦~~~ 哈哈哈哈哈哈哈哈哈哈哈哈 接下來就是收尾階段了

你覺得這就完了嗎 BUG之路纔剛剛開始~


在登陸界面中取出SP中的字段判斷是否打開了指紋登陸

在這裏發生了內存泄漏 緣由是由於當activity銷燬後dialog還存在 和這個bug搏鬥了一個下午


val isOpenFingerLogin by DelegatesExt.myPreferences(Constans.OPEN_FINGERPRINT_LOGIN, false)
login_btn.setOnClickListener {pwlogin(userEdit.text.toString(),passEdit.text.toString()) }
if(isOpenFingerLogin){
    login_btn.postDelayed({ fplogin() },1000)
}複製代碼

在主界面中判斷設備是否支持指紋識別 支持就經過拋出彈窗詢問是否打開 

manager = FingerprintManager(this,callback)
if(manager.isSupportFingerLogin&&!isOpenFingerLogin){
    fingerSwitch.postDelayed({ showFingerprintLoginDialog() },500)
}else if(!manager.isSupportFingerLogin)
{
    Toast.makeText(this,"您的設備不支持指紋識別",Toast.LENGTH_SHORT).show()
}複製代碼

設置一個開關 封裝一下邏輯 指紋識別的開關 

fingerSwitch.setOnClickListener {
    if(isOpenView){
        closeFingerprintLogin()
    }else{
        showFingerprintLoginDialog()
    }
}複製代碼

最後就大功告成啦~~~~~  哈哈哈哈哈哈哈哈哈哈哈哈哈哈

最後感謝你們的瀏覽 有什麼問題能夠留言交流

相關文章
相關標籤/搜索