旋转手机(生物识别提示框激活状态下)后UI元素无法正常更新的问题求助
Hey, great job narrowing down the issue to a simple test case—that makes it way easier to diagnose! Let's break down why this is happening and how to fix it.
问题根源
核心原因在于:当你旋转设备时,Android会**重新创建Activity(或Fragment)**来处理配置变更。而生物识别提示框的onAuthenticationSucceeded回调是绑定到启动提示框的那个原始Activity/Fragment实例上的。当旋转完成后,这个原始实例(以及它对应的TextView d8fe4ae)已经被销毁,取而代之的是一个全新的实例(对应TextView 4200ade)。但你的回调还在试图更新那个已经脱离UI的旧视图,所以自然看不到任何变化。
解决方案
方案1:使用ViewModel持久化认证状态(推荐)
ViewModel是Android官方设计用来跨配置变更保存数据的组件,完美适配这个场景。它能在Activity/Fragment重建时保持存活,让我们把UI状态和生命周期解耦。
步骤如下:
- 创建一个ViewModel类,用LiveData保存认证成功的状态
- 在Activity/Fragment中观察这个LiveData,一旦状态更新就修改当前的TextView
- 在生物识别回调中,只更新ViewModel里的LiveData,不直接操作视图
示例代码:
// ViewModel类 class AuthViewModel : ViewModel() { // 用MutableLiveData保存认证成功状态 val authSuccess = MutableLiveData<Boolean>() } // 在你的Activity中 private lateinit var viewModel: AuthViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 初始化ViewModel viewModel = ViewModelProvider(this)[AuthViewModel::class.java] // 观察认证状态变化,更新当前的TextView viewModel.authSuccess.observe(this) { isSuccess -> if (isSuccess) { findViewById<TextView>(R.id.your_text_view).text = "认证成功!" // 重置状态,避免重复触发 viewModel.authSuccess.value = false } } // 启动生物识别提示框的按钮点击事件 findViewById<Button>(R.id.btn_show_biometric).setOnClickListener { val promptInfo = BiometricPrompt.PromptInfo.Builder() .setTitle("请进行身份验证") .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG) .build() val biometricPrompt = BiometricPrompt(this, object : BiometricPrompt.AuthenticationCallback() { override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { super.onAuthenticationSucceeded(result) // 只更新ViewModel的LiveData,不直接操作视图 viewModel.authSuccess.postValue(true) } }) biometricPrompt.authenticate(promptInfo) } }
为什么这个方案有效?因为ViewModel在旋转后依然存活,新的Activity实例会观察同一个LiveData。当回调更新LiveData时,新实例会立即感知到变化,并更新当前的TextView。
方案2:锁定Activity方向(快速修复,不推荐)
如果你的应用不需要支持旋转,可以在AndroidManifest.xml中给目标Activity添加android:screenOrientation="portrait"(或landscape),这样就能避免Activity因旋转重建,原始视图实例会保持有效。
但这个方案不推荐,因为它忽略了用户的方向偏好和无障碍需求,属于妥协性的临时修复。
方案3:检查Activity生命周期状态(不太可靠)
你可以在回调中先检查Activity是否处于活跃状态,再尝试更新视图,但这种方式不如ViewModel可靠,因为回调还是绑定在旧的Activity实例上:
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { super.onAuthenticationSucceeded(result) // 检查当前Activity是否仍处于resumed状态 if (isResumed) { runOnUiThread { findViewById<TextView>(R.id.your_text_view).text = "认证成功!" } } }
这种方式可能会出现findViewById返回null的情况,所以还是ViewModel方案更稳妥。
总结
ViewModel方案是最清晰、最易维护的解决方式,它能正确地将UI状态与Activity/Fragment的生命周期分离,确保无论发生什么配置变更,UI都能正确响应认证状态的变化。
内容来源于stack exchange

