使用Hilt注入Activity实现BiometricPrompt遇异常的解决方案咨询
你的核心问题是将绑定Activity生命周期的BiometricPromptManager声明为了Singleton。SingletonComponent的依赖在Application初始化阶段就会被创建,此时注入的Activity实例还未完成与Application的绑定(未走到onCreate生命周期),而BiometricPrompt.authenticate()内部会依赖Activity的ViewModelStore,因此抛出IllegalStateException。
以下是几个符合架构规范的最佳解决方案:
方案1:使用Activity-scoped的依赖注入(推荐)
BiometricPrompt本身与Activity强绑定,生命周期完全依赖Activity,因此不应该做成单例。修改Hilt Module的作用域为ActivityComponent,确保每次Activity创建时生成对应的BiometricPromptManager实例,且实例初始化时Activity已完成绑定:
@Module @InstallIn(ActivityComponent::class) // 改为Activity作用域 object BiometricModule { @Provides // 去掉@Singleton,默认是Activity-scoped fun provideBiometricPromptManager( activity: AppCompatActivity // 注入通用的AppCompatActivity而非具体MainActivity ): BiometricPromptManager { return BiometricPromptManagerImpl(activity) } }
在需要使用的组件(如ViewModel)中直接注入BiometricPromptManager即可,此时获取的实例与当前Activity绑定,且Activity已处于可用状态。
方案2:通过Provider延迟获取Activity实例
如果必须在非Activity-scoped的类中使用(如Singleton的Repository),可以通过Provider<AppCompatActivity>延迟获取Activity实例,确保调用时Activity已完成初始化:
class BiometricPromptManagerImpl( private val activityProvider: Provider<AppCompatActivity> // 使用Provider延迟获取 ) : BiometricPromptManager { fun showBiometricPrompt(...) { val activity = activityProvider.get() check(activity.isAttachedToApplication) { "Activity未绑定Application" } val prompt = BiometricPrompt(activity, ...) prompt.authenticate(...) } }
对应的Module:
@Module @InstallIn(SingletonComponent::class) object BiometricModule { @Provides @Singleton fun provideBiometricPromptManager( activityProvider: Provider<AppCompatActivity> ): BiometricPromptManager { return BiometricPromptManagerImpl(activityProvider) } }
注意:这种方式需要确保调用
showBiometricPrompt时存在活跃的Activity实例,适合UI触发的场景。
方案3:Compose项目使用Biometric Compose库(无侵入式)
如果你的项目使用Jetpack Compose,可直接使用androidx.biometric:biometric-compose库,无需直接持有Activity实例,完全符合Compose架构与整洁分层原则:
- 添加依赖:
implementation "androidx.biometric:biometric-compose:1.0.0-alpha06"
- 在Composable中使用:
@Composable fun BiometricAuthScreen(onAuthSuccess: () -> Unit) { val biometricPrompt = rememberBiometricPrompt( onAuthError = { errorCode, errString -> /* 处理错误 */ }, onAuthFailed = { /* 处理失败 */ }, onAuthSucceeded = { onAuthSuccess() } ) Button(onClick = { biometricPrompt.authenticate(PromptInfo.Builder() .setTitle("身份验证") .setSubtitle("请验证指纹") .build()) }) { Text("验证指纹") } }
这种方式下,业务层只需定义回调逻辑,UI层负责Biometric的显示,完全隔离了分层依赖。
内容的提问来源于stack exchange,提问作者Thomas Constantin

