我正在使用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
)