9、Android 10以下兼容处理:使用FingerprintManagerCompat、AndroidX兼容库、版本判断逻辑

好,咱们接着聊。上一章我讲了Android 10以上的生物识别API,那Android 10以下的设备怎么办?

说实话,这才是真正的「硬仗」。我翻过不少项目的代码,发现很多人在低版本兼容上栽了跟头。有的直接crash,有的指纹识别弹不出来,有的干脆不支持人脸——嗯,这些坑我都踩过。

为什么需要兼容处理?

Android的生物识别API经历了几个阶段:

  • Android 6.0 (API 23):首次引入FingerprintManager,只支持指纹
  • Android 9.0 (API 28):引入BiometricPrompt,支持人脸+指纹+虹膜
  • Android 10 (API 29):BiometricPrompt正式稳定,废弃旧API

你想想看,如果你的App要覆盖Android 6到Android 13的用户,那你就得同时处理三套API。我见过最离谱的项目,直接只在Android 10以上跑,低版本用户全部弹Toast「您的设备不支持」——这显然不行。

核心思路:运行时判断API版本,高版本用BiometricPrompt,低版本用FingerprintManagerCompat + AndroidX兼容库。

第一步:引入AndroidX兼容库

我个人习惯用AndroidX的biometric库,它已经帮我们封装好了大部分兼容逻辑。但注意,这个库只支持API 28以上。对于API 23-27,我们还得自己处理。

build.gradle里添加依赖:

dependencies {
    // AndroidX 生物识别库(API 28+)
    implementation 'androidx.biometric:biometric:1.2.0-alpha05'
    
    // 兼容库(用于FingerprintManagerCompat)
    implementation 'androidx.core:core:1.9.0'
}

小提示:biometric库的1.2.0版本开始,已经内置了对FingerprintManagerCompat的封装。但如果你要自己控制细节,还是建议手动判断版本。

第二步:版本判断逻辑

嗯,这里要写一个工具类。我习惯把它叫做BiometricUtils,专门处理版本判断和兼容。

public class BiometricUtils {
    
    /**
     * 判断当前设备是否支持生物识别
     */
    public static boolean isBiometricSupported(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            // Android 9+ 使用BiometricManager
            BiometricManager manager = BiometricManager.from(context);
            return manager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS;
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // Android 6-8 使用FingerprintManagerCompat
            FingerprintManagerCompat compat = FingerprintManagerCompat.from(context);
            return compat.isHardwareDetected() && compat.hasEnrolledFingerprints();
        } else {
            return false;
        }
    }
    
    /**
     * 获取可用的认证方式
     */
    public static int getAvailableAuthenticators(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            BiometricManager manager = BiometricManager.from(context);
            return manager.getAvailableAuthenticators();
        } else {
            // 低版本只支持指纹
            return BiometricManager.Authenticators.BIOMETRIC_FINGERPRINT;
        }
    }
}

这里有个细节:FingerprintManagerCompat只支持指纹,不支持人脸。所以如果你在Android 6-8的设备上想做人脸识别,那基本没戏——硬件层面就不支持。我当年在一个项目里,产品经理非要让Android 7的手机支持人脸解锁,我跟他解释了半天「硬件不支持」,最后他妥协了。

第三步:使用FingerprintManagerCompat进行指纹认证

对于Android 6-8的设备,我们只能用指纹。代码长这样:

public class LegacyFingerprintHelper {
    
    private FingerprintManagerCompat manager;
    private CancellationSignal cancellationSignal;
    
    public LegacyFingerprintHelper(Context context) {
        manager = FingerprintManagerCompat.from(context);
    }
    
    public void startAuth(FingerprintManagerCompat.AuthenticationCallback callback) {
        if (cancellationSignal != null && !cancellationSignal.isCanceled()) {
            cancellationSignal.cancel();
        }
        cancellationSignal = new CancellationSignal();
        
        // 注意:这里需要API 23+,且用户已录入指纹
        manager.authenticate(null, 0, cancellationSignal, callback, null);
    }
    
    public void stopAuth() {
        if (cancellationSignal != null) {
            cancellationSignal.cancel();
            cancellationSignal = null;
        }
    }
}

避坑指南:我曾经在authenticate()方法里传了nullCryptoObject,结果在某些华为手机上直接闪退。后来查文档才发现,部分厂商对CryptoObject有强校验。建议至少传一个简单的null,但最好用KeyGenParameterSpec生成一个。

第四步:统一调用入口

为了让上层调用者不用关心版本,我封装了一个统一的认证类:

public class BiometricAuthManager {
    
    private Context context;
    
    public BiometricAuthManager(Context context) {
        this.context = context.getApplicationContext();
    }
    
    public void authenticate(BiometricAuthCallback callback) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            // 高版本使用BiometricPrompt
            showBiometricPrompt(callback);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // 低版本使用FingerprintManagerCompat
            showFingerprintDialog(callback);
        } else {
            callback.onError("设备不支持生物识别");
        }
    }
    
    private void showBiometricPrompt(BiometricAuthCallback callback) {
        BiometricPrompt.PromptInfo info = new BiometricPrompt.PromptInfo.Builder()
                .setTitle("验证身份")
                .setSubtitle("请使用指纹或人脸验证")
                .setNegativeButtonText("取消")
                .build();
        
        BiometricPrompt prompt = new BiometricPrompt(
                (FragmentActivity) context,
                ContextCompat.getMainExecutor(context),
                new BiometricPrompt.AuthenticationCallback() {
                    @Override
                    public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
                        callback.onSuccess();
                    }
                    
                    @Override
                    public void onAuthenticationError(int errorCode, CharSequence errString) {
                        callback.onError(errString.toString());
                    }
                    
                    @Override
                    public void onAuthenticationFailed() {
                        callback.onFailed();
                    }
                });
        
        prompt.authenticate(info);
    }
    
    private void showFingerprintDialog(BiometricAuthCallback callback) {
        // 这里可以弹出自定义指纹对话框
        // 或者直接调用FingerprintManagerCompat
        LegacyFingerprintHelper helper = new LegacyFingerprintHelper(context);
        helper.startAuth(new FingerprintManagerCompat.AuthenticationCallback() {
            @Override
            public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
                callback.onSuccess();
            }
            
            @Override
            public void onAuthenticationError(int errMsgId, CharSequence errString) {
                callback.onError(errString.toString());
            }
            
            @Override
            public void onAuthenticationFailed() {
                callback.onFailed();
            }
        });
    }
    
    public interface BiometricAuthCallback {
        void onSuccess();
        void onError(String error);
        void onFailed();
    }
}

核心逻辑流程图

下面这张图,是我整理的整体兼容策略。你看一眼就明白了:

Android 生物识别兼容处理流程 开始认证 API >= 28? BiometricPrompt Android 9+ API >= 23? FingerprintManagerCompat Android 6-8 不支持生物识别 Android 5- 认证结果回调

常见问题与避坑

问题 原因 解决方案
FingerprintManagerCompat找不到类 没有引入AndroidX core库 添加implementation 'androidx.core:core:1.9.0'
低版本设备上弹不出指纹对话框 没有检查hasEnrolledFingerprints() 先检查用户是否已录入指纹
部分厂商设备上CryptoObject为null时崩溃 厂商对CryptoObject有强校验 使用KeyGenParameterSpec生成CryptoObject
Android 9设备上BiometricPrompt不显示人脸选项 没有设置setAllowedAuthenticators() 显式指定BIOMETRIC_STRONG | BIOMETRIC_WEAK

我的经验:在低版本兼容上,我建议你多做一步——在用户点击「指纹验证」之前,先调用isBiometricSupported()检查。如果返回false,直接弹Toast提示「您的设备不支持」,而不是让用户点了之后才报错。用户体验会好很多。

总结一下

Android 10以下的兼容处理,说白了就是三件事:

  1. 版本判断:用Build.VERSION.SDK_INT区分API 28+、API 23-27、API 22-
  2. API选择:高版本用BiometricPrompt,低版本用FingerprintManagerCompat
  3. 统一封装:对外暴露一个接口,内部根据版本走不同分支

嗯,这套方案我在好几个项目里验证过,覆盖了从Android 6到Android 13的机型,没出过兼容性问题。你照着这个思路写,应该不会踩坑。

最后提醒一句:千万不要在Android 6以下的设备上调用任何生物识别API——那些设备连指纹硬件都没有,调用就是crash。老老实实走密码验证吧。

公众号:蓝海资料掘金营,微信deep3321