Android指纹识别,提升APP用户体验,从这里开始

 
 

本文由 左海龙 授权投稿
原文链接:https://blog.csdn.net/hailong0529/article/details/95406183

写在前面

指纹识别大家都不陌生,现在比较新的安卓手机大多都已经支持面部识别了,指纹识别更是主流安卓手机的标配功能。这两个功能可以说用过都说好,确实是方便快捷。

不过大家观察一下会发现,这些手机的指纹识别和面部识别也就是支持一下手机的锁屏解锁而已,数量巨大的APP对于这两个技术的应用可以说比较少。这何尝不是APP良好体验性的损失呢?

庆幸的是,基于 Google API 实现指纹识别的基础性功能并不复杂。

指纹识别的兼容性和安全性问题

除了实现指纹识别的基础性功能,我认为开发者还需要关注和选择性处理的问题有两个:兼容性和安全性。

为什么说选择性处理?

首先说兼容性,指纹识别的 API 是 Google 在 Android 6.0 开放出来的。

在 Android 6.0 以下的系统上,某些手机厂商自行支持了指纹识别,如果我们的 APP 要兼容这些设备,就还要集成厂商的指纹识别的SDK,这是最大的兼容性问题。不过,现在 Android 6.0 以下的设备已经很少了,其中支持指纹识别的设备就更少了,不对其进行兼容,我认为也是可以的。

在Android 6.0 以上的系统上,由于厂商对 Android 系统和指纹识别模块的定制化普遍,导致会出现一些兼容性问题。这个没有什么好的办法,就需要开发者见招拆招了。已经踩过坑的开发者很多,大家可以到网上搜索相关的文章看。

然后说下安全性,由于已添加的指纹是存储在手机上的,Google API 验证指纹后仅仅返回 true 或者 false,我们是很难无条件相信这个识别结果的。比如说用户的手机 root 了或者是自定制设备,指纹识别是有可能被劫持进而返回有误的识别结果的。

好在这种情况发生的概率比较低。如果指纹识别的应用场景非交易非支付,仅仅是类似于 “启动 APP 进行指纹验证” 这样的情况的话,Google API 提供的指纹识别就够用了。

关于兼容性和安全性的问题,本文不过多探讨了,给大家推荐一篇文章,同时也请大家关注文章中提到的支付宝和腾讯的处理方式,及其开源情况:

如何在复杂业务场景中优雅实现Android指纹验证?

指纹识别 API 的版本演进

在 Android 6.0(Android M Api23),Android 系统开放了指纹识别的api,存在于  android.hardware.fingerprint包下,核心类是FingerprintManager,提供了基础的指纹识别的功能。要注意的是,FingerprintManager在 Android 9.0(Android P Api28)做了 @Deprecated 标记,将被弃用。

后来,在android.support.v4.hardware.fingerprint包和 androidx.core.hardware.fingerprint包中,FingerprintManager升级为了 FingerprintManagerCompat,对功能进行了增强,也做了一些兼容性的处理,比如增加了系统版本号的判断,对指纹支持加密处理等。实际上阅读源码会发现,他的核心功能还是调用 FingerprintManager 实现的。

再之后,在 Android 9.0(Android P Api 28),Google 对生物识别进行了进一步增强,开放了以 BiometricPrompt 为核心的新 Api,存在于 androidx.biometric 包和android.hardware.biometrics包下,Google 在开发者文档中是这样解释的:

On devices running P and above, this will show a system-provided authentication prompt, using a device's supported biometric (fingerprint, iris, face, etc).

大意是,在 Android P 及以上版本的系统中,BiometricPrompt 将展现一个由系统提供的验证提示,用于支持设备提供的生物识别,包括指纹、虹膜、面部等。

目前来看,虹膜和面部等生物识别 Api 尚未开放,仅支持指纹识别,不过在指纹识别上进行了统一,比如要求使用统一的指纹识别 UI ,不允许开发者自定义了。

指纹识别关键方法 authenticate

这是指纹识别中最核心的方法,用于拉起指纹识别扫描器进行指纹识别。

FingerprintManagerCompat 中 authenticate() 方法为例,开发者文档中是这样定义的:

640?wx_fmt=png
authenticate
640?wx_fmt=png
authenticate parameters

解释一下各个参数:
    /**     * A wrapper class for the crypto objects supported by FingerprintManager. Currently the     * framework supports {@link Signature} and {@link Cipher} objects.     */

大意是,这是一个密码对象的包装类,当前支持 Signature 形式和 Cipher 形式的密码对象加密。

作用是,指纹扫描器会使用这个对象判断指纹认证结果的合法性。Android 6.0 是 @Nullable,但不建议传 null,且在 Android 9.0 之后就是 @NonNull 了。

我的代码中提供了一个 Cipher 的帮助类,可用来创建一个 Cipher 对象,可参考使用。

指纹识别的实践

在指纹识别功能的实践中,我将其做成了开源库发布在了 Github 上,可通过 gradle 进行依赖,使用方法比较简单,两三行代码,再传入一个验证结果监听即可。

下面对部分实现过程做一下介绍,详细的 Api 及源码请移步 Github。

Github 地址:https://github.com/ZuoHailong/BiometricPrompt

示例

Android 6.0 指纹识别框,开发者自定义:

640?wx_fmt=png
自定义指纹识别框

Android 9.0 指纹识别框,系统提供:
640?wx_fmt=png
系统指纹识别框

其中指纹 icon 和取消按钮的文字颜色,由属性 colorPrimary 的颜色值决定。


指纹识别管理类

FingerprintVerifyManager 是指纹识别库的入口,对指纹识别进行管理,通过 FingerprintVerifyManager.Builder 对指纹识别进行初始化。

在这个类中,有根据手机系统版本调用不同的指纹识别 Api(FingerprintManagerCompat 或者 BiometricPrompt),其中 BiometricPrompt 支持开启或者关闭,默认关闭。

public FingerprintVerifyManager(Builder builder) {        IFingerprint fingerprint;        // >= Android P        if (AndrVersionUtil.isAboveAndrP()) {            //在 Android P 上是否展示系统提供的识别框            if (builder.enableAndroidP)                fingerprint = FingerprintAndrP.newInstance();            else                fingerprint = FingerprintAndrM.newInstance();        } else if (AndrVersionUtil.isAboveAndrM()) {// Android 6.0 =< Version            fingerprint = FingerprintAndrM.newInstance();        } else {// < Android 6.0 ,官方未开放指纹识别,某些机型自行支持的情况暂不做处理            builder.callback.onError(builder.context.getString(R.string.biometricprompt_verify_error_below_m));            return;        }        ……        fingerprint.authenticate(builder.context, bean, builder.callback);    }
        IFingerprint fingerprint;
        // >= Android P
        if (AndrVersionUtil.isAboveAndrP()) {
            //在 Android P 上是否展示系统提供的识别框
            if (builder.enableAndroidP)
                fingerprint = FingerprintAndrP.newInstance();
            else
                fingerprint = FingerprintAndrM.newInstance();
        } else if (AndrVersionUtil.isAboveAndrM()) {// Android 6.0 =< Version
            fingerprint = FingerprintAndrM.newInstance();
        } else {// < Android 6.0 ,官方未开放指纹识别,某些机型自行支持的情况暂不做处理
            builder.callback.onError(builder.context.getString(R.string.biometricprompt_verify_error_below_m));
            return;
        }

        ……

        fingerprint.authenticate(builder.context, bean, builder.callback);
    }

其中 IFingerprint 是指纹识别的接口,兼容 Android 6.0 的 FingerprintAndrM 和兼容 Android 9.0 的 FingerprintAndrP 都实现了此接口。

public interface IFingerprint {    /**     * 初始化并调起指纹识别     *     * @param context     * @param verificationDialogStyleBean 指纹识别框样式     * @param callback 通知开发者指纹识别结果     */    void authenticate(Activity context, VerificationDialogStyleBean verificationDialogStyleBean, FingerprintCallback callback);}interface IFingerprint {

    /**
     * 初始化并调起指纹识别
     *
     * @param context
     * @param verificationDialogStyleBean 指纹识别框样式
     * @param callback 通知开发者指纹识别结果
     */

    void authenticate(Activity context, VerificationDialogStyleBean verificationDialogStyleBean, FingerprintCallback callback);

}

基于Android 6.0 实现指纹识别

上文有提及,FingerprintAndrM 是基于 Android 6.0 的具体的指纹识别实现类:

@RequiresApi(api = Build.VERSION_CODES.M)public class FingerprintAndrM implements IFingerprint {    private final String TAG = FingerprintAndrM.class.getName();    private Activity context;    private static FingerprintAndrM fingerprintAndrM;    //指纹验证框    private static FingerprintDialog fingerprintDialog;    //指向调用者的指纹回调    private FingerprintCallback fingerprintCallback;    //用于取消扫描器的扫描动作    private CancellationSignal cancellationSignal;    //指纹加密    private static FingerprintManagerCompat.CryptoObject cryptoObject;    //Android 6.0 指纹管理    private FingerprintManagerCompat fingerprintManagerCompat;    @Override    public void authenticate(Activity context, VerificationDialogStyleBean bean, FingerprintCallback callback) {        //判断指纹识别是否可用        if (!canAuthenticate(context, callback))            return;        this.context = context;        this.fingerprintCallback = callback;        //Android 6.0 指纹管理 实例化        fingerprintManagerCompat = FingerprintManagerCompat.from(context);        //取消扫描,每次取消后需要重新创建新示例        cancellationSignal = new CancellationSignal();        cancellationSignal.setOnCancelListener(() -> fingerprintDialog.dismiss());        //调起指纹验证        fingerprintManagerCompat.authenticate(cryptoObject, 0, cancellationSignal, authenticationCallback, null);        //指纹验证框        fingerprintDialog = FingerprintDialog.newInstance(context).setActionListener(dialogActionListener).setDialogStyle(bean);        fingerprintDialog.show(context.getFragmentManager(), TAG);    }    public static FingerprintAndrM newInstance() {        if (fingerprintAndrM == null) {            synchronized (FingerprintAndrM.class) {                if (fingerprintAndrM == null) {                    fingerprintAndrM = new FingerprintAndrM();                }            }        }        //指纹加密,提前进行Cipher初始化,防止指纹认证时还没有初始化完成        try {            cryptoObject = new FingerprintManagerCompat.CryptoObject(new CipherHelper().createCipher());        } catch (Exception e) {            e.printStackTrace();        }        return fingerprintAndrM;    }    /**     * 指纹验证框按键监听     */    private FingerprintDialog.OnDialogActionListener dialogActionListener = new FingerprintDialog.OnDialogActionListener() {        @Override        public void onUsepwd() {            if (fingerprintCallback != null)                fingerprintCallback.onUsepwd();        }        @Override        public void onCancle() {//取消指纹验证,通知调用者            if (fingerprintCallback != null)                fingerprintCallback.onCancel();        }        @Override        public void onDismiss() {//验证框消失,取消指纹验证            if (cancellationSignal != null && !cancellationSignal.isCanceled())                cancellationSignal.cancel();        }    };    /**     * 指纹验证结果回调     */    private FingerprintManagerCompat.AuthenticationCallback authenticationCallback = new FingerprintManagerCompat.AuthenticationCallback() {        @Override        public void onAuthenticationError(int errMsgId, CharSequence errString) {            super.onAuthenticationError(errMsgId, errString);            fingerprintDialog.setTip(errString.toString(), R.color.biometricprompt_color_FF5555);        }        @Override        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {            super.onAuthenticationHelp(helpMsgId, helpString);            fingerprintDialog.setTip(helpString.toString(), R.color.biometricprompt_color_FF5555);        }        @Override        public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {            super.onAuthenticationSucceeded(result);            fingerprintDialog.setTip(context.getString(R.string.biometricprompt_verify_success), R.color.biometricprompt_color_82C785);            fingerprintCallback.onSucceeded();            fingerprintDialog.dismiss();        }        @Override        public void onAuthenticationFailed() {            super.onAuthenticationFailed();            fingerprintDialog.setTip(context.getString(R.string.biometricprompt_verify_failed), R.color.biometricprompt_color_FF5555);            fingerprintCallback.onFailed();        }    };    /*     * 在 Android Q,Google 提供了 Api BiometricManager.canAuthenticate() 用来检测指纹识别硬件是否可用及是否添加指纹     * 不过尚未开放,标记为"Stub"(存根)     * 所以暂时还是需要使用 Andorid 6.0 的 Api 进行判断     * */    private boolean canAuthenticate(Context context, FingerprintCallback fingerprintCallback) {        /*         * 硬件是否支持指纹识别         * */        if (!FingerprintManagerCompat.from(context).isHardwareDetected()) {            fingerprintCallback.onError(FingerprintManager.FINGERPRINT_ERROR_HW_NOT_PRESENT, context.getString(R.string.biometricprompt_verify_error_no_hardware));            return false;        }        //是否已添加指纹        if (!FingerprintManagerCompat.from(context).hasEnrolledFingerprints()) {            fingerprintCallback.onNoneEnrolled();            return false;        }        return true;    }}
public class FingerprintAndrM implements IFingerprint {

    private final String TAG = FingerprintAndrM.class.getName();
    private Activity context;

    private static FingerprintAndrM fingerprintAndrM;
    //指纹验证框
    private static FingerprintDialog fingerprintDialog;
    //指向调用者的指纹回调
    private FingerprintCallback fingerprintCallback;

    //用于取消扫描器的扫描动作
    private CancellationSignal cancellationSignal;
    //指纹加密
    private static FingerprintManagerCompat.CryptoObject cryptoObject;
    //Android 6.0 指纹管理
    private FingerprintManagerCompat fingerprintManagerCompat;

    @Override
    public void authenticate(Activity context, VerificationDialogStyleBean bean, FingerprintCallback callback) {

        //判断指纹识别是否可用
        if (!canAuthenticate(context, callback))
            return;

        this.context = context;
        this.fingerprintCallback = callback;
        //Android 6.0 指纹管理 实例化
        fingerprintManagerCompat = FingerprintManagerCompat.from(context);

        //取消扫描,每次取消后需要重新创建新示例
        cancellationSignal = new CancellationSignal();
        cancellationSignal.setOnCancelListener(() -> fingerprintDialog.dismiss());

        //调起指纹验证
        fingerprintManagerCompat.authenticate(cryptoObject, 0, cancellationSignal, authenticationCallback, null);
        //指纹验证框
        fingerprintDialog = FingerprintDialog.newInstance(context).setActionListener(dialogActionListener).setDialogStyle(bean);
        fingerprintDialog.show(context.getFragmentManager(), TAG);
    }

    public static FingerprintAndrM newInstance() {
        if (fingerprintAndrM == null) {
            synchronized (FingerprintAndrM.class) {
                if (fingerprintAndrM == null) {
                    fingerprintAndrM = new FingerprintAndrM();
                }
            }
        }
        //指纹加密,提前进行Cipher初始化,防止指纹认证时还没有初始化完成
        try {
            cryptoObject = new FingerprintManagerCompat.CryptoObject(new CipherHelper().createCipher());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return fingerprintAndrM;
    }

    /**
     * 指纹验证框按键监听
     */

    private FingerprintDialog.OnDialogActionListener dialogActionListener = new FingerprintDialog.OnDialogActionListener() {
        @Override
        public void onUsepwd() {
            if (fingerprintCallback != null)
                fingerprintCallback.onUsepwd();
        }

        @Override
        public void onCancle() {//取消指纹验证,通知调用者
            if (fingerprintCallback != null)
                fingerprintCallback.onCancel();
        }

        @Override
        public void onDismiss() {//验证框消失,取消指纹验证
            if (cancellationSignal != null && !cancellationSignal.isCanceled())
                cancellationSignal.cancel();
        }
    };

    /**
     * 指纹验证结果回调
     */

    private FingerprintManagerCompat.AuthenticationCallback authenticationCallback = new FingerprintManagerCompat.AuthenticationCallback() {
        @Override
        public void onAuthenticationError(int errMsgId, CharSequence errString) {
            super.onAuthenticationError(errMsgId, errString);
            fingerprintDialog.setTip(errString.toString(), R.color.biometricprompt_color_FF5555);
        }

        @Override
        public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
            super.onAuthenticationHelp(helpMsgId, helpString);
            fingerprintDialog.setTip(helpString.toString(), R.color.biometricprompt_color_FF5555);
        }

        @Override
        public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
            super.onAuthenticationSucceeded(result);
            fingerprintDialog.setTip(context.getString(R.string.biometricprompt_verify_success), R.color.biometricprompt_color_82C785);
            fingerprintCallback.onSucceeded();
            fingerprintDialog.dismiss();
        }

        @Override
        public void onAuthenticationFailed() {
            super.onAuthenticationFailed();
            fingerprintDialog.setTip(context.getString(R.string.biometricprompt_verify_failed), R.color.biometricprompt_color_FF5555);
            fingerprintCallback.onFailed();
        }
    };

    /*
     * 在 Android Q,Google 提供了 Api BiometricManager.canAuthenticate() 用来检测指纹识别硬件是否可用及是否添加指纹
     * 不过尚未开放,标记为"Stub"(存根)
     * 所以暂时还是需要使用 Andorid 6.0 的 Api 进行判断
     * */

    private boolean canAuthenticate(Context context, FingerprintCallback fingerprintCallback) {
        /*
         * 硬件是否支持指纹识别
         * */

        if (!FingerprintManagerCompat.from(context).isHardwareDetected()) {
            fingerprintCallback.onError(FingerprintManager.FINGERPRINT_ERROR_HW_NOT_PRESENT, context.getString(R.string.biometricprompt_verify_error_no_hardware));
            return false;
        }
        //是否已添加指纹
        if (!FingerprintManagerCompat.from(context).hasEnrolledFingerprints()) {
            fingerprintCallback.onNoneEnrolled();
            return false;
        }
        return true;
    }

}

这里面要重点关注 CancellationSignal 与指纹识别框的关联,也就是识别框消失,就一定要取消指纹扫描器的扫描操作,否则在超时时间内,用户将无法再次拉起指纹识别(尽管可以弹出指纹识别框)。

基于Android 9.0 实现指纹识别

上文有提及,FingerprintAndrP 是基于 Android 9.0 的具体的指纹识别实现类:

@RequiresApi(api = Build.VERSION_CODES.P)public class FingerprintAndrP implements IFingerprint {    private static FingerprintAndrP fingerprintAndrP;    //指向调用者的指纹回调    private FingerprintCallback fingerprintCallback;    //用于取消扫描器的扫描动作    private CancellationSignal cancellationSignal;    //指纹加密    private static BiometricPrompt.CryptoObject cryptoObject;    @Override    public void authenticate(Activity context, VerificationDialogStyleBean verificationDialogStyleBean, FingerprintCallback callback) {        //判断指纹识别是否可用        if (!canAuthenticate(context, callback))            return;        this.fingerprintCallback = callback;        /*         * 初始化 BiometricPrompt.Builder         */        ……        //构建 BiometricPrompt        BiometricPrompt biometricPrompt = builder.build();        //取消扫描,每次取消后需要重新创建新示例        cancellationSignal = new CancellationSignal();        cancellationSignal.setOnCancelListener(() -> {        });        /*         * 拉起指纹验证模块,等待验证         * Executor:         * context.getMainExecutor()         */        biometricPrompt.authenticate(cryptoObject, cancellationSignal, context.getMainExecutor(), authenticationCallback);    }    public static FingerprintAndrP newInstance() {        if (fingerprintAndrP == null) {            synchronized (FingerprintAndrM.class) {                if (fingerprintAndrP == null) {                    fingerprintAndrP = new FingerprintAndrP();                }            }        }        //指纹加密,提前进行Cipher初始化,防止指纹认证时还没有初始化完成        try {            cryptoObject = new BiometricPrompt.CryptoObject(new CipherHelper().createCipher());        } catch (Exception e) {            e.printStackTrace();        }        return fingerprintAndrP;    }    /**     * 认证结果回调     */    private BiometricPrompt.AuthenticationCallback authenticationCallback = new BiometricPrompt.AuthenticationCallback() {        @Override        public void onAuthenticationError(int errorCode, CharSequence errString) {            super.onAuthenticationError(errorCode, errString);            if (fingerprintCallback != null) {                if (errorCode == 5) {//用户取消指纹验证,不必向用户抛提示信息                    fingerprintCallback.onCancel();                    return;                }                fingerprintCallback.onError(errorCode, errString.toString());            }        }        @Override        public void onAuthenticationHelp(int helpCode, CharSequence helpString) {            super.onAuthenticationHelp(helpCode, helpString);            if (fingerprintCallback != null)                fingerprintCallback.onError(helpCode, helpString.toString());        }        @Override        public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {            super.onAuthenticationSucceeded(result);            if (fingerprintCallback != null)                fingerprintCallback.onSucceeded();        }        @Override        public void onAuthenticationFailed() {            super.onAuthenticationFailed();            if (fingerprintCallback != null)                fingerprintCallback.onFailed();        }    };    /*     * 在 Android Q,Google 提供了 Api BiometricManager.canAuthenticate() 用来检测指纹识别硬件是否可用及是否添加指纹     * 不过尚未开放,标记为"Stub"(存根)     * 所以暂时还是需要使用 Andorid 6.0 的 Api 进行判断     * */    private boolean canAuthenticate(Context context, FingerprintCallback fingerprintCallback) {        /*         * 硬件是否支持指纹识别         * */        if (!FingerprintManagerCompat.from(context).isHardwareDetected()) {            fingerprintCallback.onError(FingerprintManager.FINGERPRINT_ERROR_HW_NOT_PRESENT, context.getString(R.string.biometricprompt_verify_error_no_hardware));            return false;        }        //是否已添加指纹        if (!FingerprintManagerCompat.from(context).hasEnrolledFingerprints()) {            fingerprintCallback.onNoneEnrolled();            return false;        }        return true;    }}
public class FingerprintAndrP implements IFingerprint {

    private static FingerprintAndrP fingerprintAndrP;
    //指向调用者的指纹回调
    private FingerprintCallback fingerprintCallback;

    //用于取消扫描器的扫描动作
    private CancellationSignal cancellationSignal;
    //指纹加密
    private static BiometricPrompt.CryptoObject cryptoObject;

    @Override
    public void authenticate(Activity context, VerificationDialogStyleBean verificationDialogStyleBean, FingerprintCallback callback) {

        //判断指纹识别是否可用
        if (!canAuthenticate(context, callback))
            return;

        this.fingerprintCallback = callback;

        /*
         * 初始化 BiometricPrompt.Builder
         */

        ……

        //构建 BiometricPrompt
        BiometricPrompt biometricPrompt = builder.build();

        //取消扫描,每次取消后需要重新创建新示例
        cancellationSignal = new CancellationSignal();
        cancellationSignal.setOnCancelListener(() -> {
        });

        /*
         * 拉起指纹验证模块,等待验证
         * Executor:
         * context.getMainExecutor()
         */

        biometricPrompt.authenticate(cryptoObject, cancellationSignal, context.getMainExecutor(), authenticationCallback);
    }

    public static FingerprintAndrP newInstance() {
        if (fingerprintAndrP == null) {
            synchronized (FingerprintAndrM.class) {
                if (fingerprintAndrP == null) {
                    fingerprintAndrP = new FingerprintAndrP();
                }
            }
        }
        //指纹加密,提前进行Cipher初始化,防止指纹认证时还没有初始化完成
        try {
            cryptoObject = new BiometricPrompt.CryptoObject(new CipherHelper().createCipher());
        } catch (Exception e) {
            e.printStackTrace();
        }

        return fingerprintAndrP;
    }

    /**
     * 认证结果回调
     */

    private BiometricPrompt.AuthenticationCallback authenticationCallback = new BiometricPrompt.AuthenticationCallback() {
        @Override
        public void onAuthenticationError(int errorCode, CharSequence errString) {
            super.onAuthenticationError(errorCode, errString);
            if (fingerprintCallback != null) {
                if (errorCode == 5) {//用户取消指纹验证,不必向用户抛提示信息
                    fingerprintCallback.onCancel();
                    return;
                }
                fingerprintCallback.onError(errorCode, errString.toString());
            }
        }

        @Override
        public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
            super.onAuthenticationHelp(helpCode, helpString);
            if (fingerprintCallback != null)
                fingerprintCallback.onError(helpCode, helpString.toString());
        }

        @Override
        public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
            super.onAuthenticationSucceeded(result);
            if (fingerprintCallback != null)
                fingerprintCallback.onSucceeded();
        }

        @Override
        public void onAuthenticationFailed() {
            super.onAuthenticationFailed();
            if (fingerprintCallback != null)
                fingerprintCallback.onFailed();
        }
    };

    /*
     * 在 Android Q,Google 提供了 Api BiometricManager.canAuthenticate() 用来检测指纹识别硬件是否可用及是否添加指纹
     * 不过尚未开放,标记为"Stub"(存根)
     * 所以暂时还是需要使用 Andorid 6.0 的 Api 进行判断
     * */

    private boolean canAuthenticate(Context context, FingerprintCallback fingerprintCallback) {

        /*
         * 硬件是否支持指纹识别
         * */

        if (!FingerprintManagerCompat.from(context).isHardwareDetected()) {
            fingerprintCallback.onError(FingerprintManager.FINGERPRINT_ERROR_HW_NOT_PRESENT, context.getString(R.string.biometricprompt_verify_error_no_hardware));
            return false;
        }
        //是否已添加指纹
        if (!FingerprintManagerCompat.from(context).hasEnrolledFingerprints()) {
            fingerprintCallback.onNoneEnrolled();
            return false;
        }
        return true;
    }

}

这里需要开发者关注的有两点:

(1) Android 9.0 不允许开发者自定义指纹识别框,但系统提供的指纹识别框的灵活性堪忧。比如说,目前来看,系统只允许在识别框出现一个按钮,放了 “取消” 就不能放 “密码验证” ,放了 “密码验证” 就不能放 “取消”。(尴尬脸……)

(2) 系统提供的指纹识别框只能在界面底部,不可以上下居中。但在某些手机上(如OPPO reno),指纹传感器也是在界面底部,当拉起指纹识别时,会在指纹传感器的位置显示一个指纹图标,以提示用户在哪下指。然而,系统提供的指纹识别框上也有一个指纹图标,这两个指纹图标就发生了重合或者离的很近。(尴尬脸……)

示例:

640?wx_fmt=jpeg
指纹图标重合

鉴于以上问题,指纹开源库提供了一个方法 builder.enableAndroidP(boolean enableAndroidP) ,允许调用者开启或者关闭 Android 9.0 系统提供的指纹识别框。当关闭时,将使用 Android 6.0 的指纹识别 Api,并使用自定义的指纹识别框。


指纹识别库 Github 地址:https://github.com/ZuoHailong/BiometricPrompt

感谢

https://developer.android.google.cn/reference

https://www.jianshu.com/p/1eae12582a31

https://www.jianshu.com/p/ed880f35f97f





推荐阅读
谈谈我最近的一些想法
Android多线程误区
ConstraintLayout,看完一篇就够了吗?




编程·思维·职场
欢迎扫码关注

640?wx_fmt=jpeg



  在看也是一种认可640?wx_fmt=gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

singwhatiwanna

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值