我正在使用androidx.biometric:biometric:1.0.1,但一切正常,但是当我有一台没有生物特征传感器的设备时(或者用户未设置指纹等),并且在进行身份验证后尝试使用DeviceCredentials时,我的功能输入数据无效。

class MainActivity : AppCompatActivity() {

    private val TAG = MainActivity::class.java.name

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        findViewById<View>(R.id.first).setOnClickListener {
            authenticate(MyData(1, "first"))
        }

        findViewById<View>(R.id.second).setOnClickListener {
            authenticate(MyData(2, "second"))
        }
    }

    private fun authenticate(data: MyData) {
        Log.e(TAG, "starting auth with $data")
        val biometricPrompt = BiometricPrompt(
            this,
            ContextCompat.getMainExecutor(this),
            object : BiometricPrompt.AuthenticationCallback() {
                override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                    Log.e(TAG, "auth done : $data")
                }
            })

        val promptInfo = BiometricPrompt.PromptInfo.Builder()
            .setDeviceCredentialAllowed(true)
            .setTitle("title")
            .build()
        biometricPrompt.authenticate(promptInfo)
    }
}

data class MyData(
    val id: Int,
    val text: String
)


首先,我单击我的first按钮,进行身份验证,然后单击我的second按钮并进行身份验证,然后android logcat如下所示:
E/com.test.biometrictest.MainActivity: starting auth with MyData(id=1, text=first)
E/com.test.biometrictest.MainActivity: auth done : MyData(id=1, text=first)
E/com.test.biometrictest.MainActivity: starting auth with MyData(id=2, text=second)
E/com.test.biometrictest.MainActivity: auth done : MyData(id=1, text=first)

如您在最后一行看到的MyData ID和文本无效!调用onAuthenticationSucceeded时,autneticate函数的输入(数据)不相同!

(如果您要进行测试,请确保使用DeviceCredentials而非生物识别技术,我的意思是模式或密码,请不要设置指纹)
为什么数据在CallBack中无效?

它可以在android 10上正常工作或使用指纹

我不想使用onSaveInstanceState。

最佳答案

当您创建新的BiometricPrompt类实例时,它将在 Activity 中添加LifecycleObserver,并且据我所知,它永远不会删除它。因此,当一个 Activity 中有多个BiometricPrompt实例时,同时存在多个LifecycleObserver会导致此问题。

对于Android Q之前的设备,存在一个透明的名为DeviceCredentialHandlerActivity的 Activity 和一个名为DeviceCredentialHandlerBridge的桥接类,它们支持设备凭证身份验证。 BiometricPrompt在不同状态下管理网桥,最后在需要时以onResume状态(在离开凭据窗口后返回 Activity 时)调用回调方法。当存在多个LifecycleObserver时,第一个将处理结果并重置网桥,因此其他观察者无需执行任何操作。这就是第一个回调实现在您的代码中调用两次的原因。

解决方案:
创建LifecycleObserver类的新实例时,应从 Activity 中删除BiometricPrompt。由于无法直接访问观察者,因此您需要在此处使用反射。我根据以下解决方案修改了您的代码:

class MainActivity : AppCompatActivity() {

    private val TAG = MainActivity::class.java.name
    private var lastLifecycleObserver: LifecycleObserver? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        findViewById<View>(R.id.first).setOnClickListener {
            authenticate(MyData(1, "first"))
        }

        findViewById<View>(R.id.second).setOnClickListener {
            authenticate(MyData(2, "second"))
        }
    }

    private fun authenticate(data: MyData) {
        Log.e(TAG, "starting auth with $data")
        lastLifecycleObserver?.let {
            lifecycle.removeObserver(it)
            lastLifecycleObserver = null
        }
        val biometricPrompt = BiometricPrompt(
                this,
                ContextCompat.getMainExecutor(this),
                object : BiometricPrompt.AuthenticationCallback() {
                    override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
                        Log.e(TAG, "auth done : $data")
                    }
                })

        var field = BiometricPrompt::class.java.getDeclaredField("mLifecycleObserver")
        field.isAccessible = true
        lastLifecycleObserver = field.get(biometricPrompt) as LifecycleObserver

        val promptInfo = BiometricPrompt.PromptInfo.Builder()
                .setDeviceCredentialAllowed(true)
                .setTitle("title")
                .build()
        biometricPrompt.authenticate(promptInfo)
    }
}

data class MyData(
        val id: Int,
        val text: String
)

07-24 09:47