如何在Hilt单例Provide方法中获取Activity的Intent参数?
我是Hilt/Dagger新手,现有项目中存在一个单例MyClass,需要从调用方应用的Activity Intent Extras中获取参数。
当前模块代码如下:
@Module @InstallIn(SingletonComponent::class) class MyModule { @Provides @Singleton fun provideMyClass( @ApplicationContext appContext: Context, // @ActivityContext activityContext: Context ): MyClass{ // 这里需要从Activity的Intent Extras中获取参数 // val myParam = (activityContext as CompatActivity).intent.getStringExtra('foo') } }
使用appContext可以正常注入,但无法从Application中获取Intent参数,必须依赖Activity。其他相关类代码:
@HiltAndroidApp class MyApplication : Application() { // ... } @AndroidEntryPoint class MyActivity : ComponentActivity() { @Inject lateinit var myClass: MyClass //... private fun myFunction() { doStuff(myClass) } }
若将@ApplicationContext appContext: Context替换为@ActivityContext activityContext: Context,会出现如下错误:
error: [Dagger/MissingBinding] @dagger.hilt.android.qualifiers.ActivityContext android.content.Context cannot be provided without an @Provides-annotated method.
public abstract static class SingletonC implements HiltWrapper_AuthenticationServiceConnectionEntryPoint,
^
Note: A binding for @dagger.hilt.android.qualifiers.ActivityContext android.content.Context exists in mypackage.MyApplication_HiltComponents.ActivityC:
@dagger.hilt.android.qualifiers.ActivityContext android.content.Context is injected at
[mypackage.MyApplication_HiltComponents.SingletonC] mypackage.MyModule.provideMyClass(�, activityContext, �)
mypackage.MyClass is injected at
[mypackage.MyApplication_HiltComponents.ActivityC] mypackage.MyActivity.myClass
mypackage.MyActivity is injected at
[mypackage.MyApplication_HiltComponents.ActivityC] mypackage.MyActivity_GeneratedInjector.injectMyActivity(mypackage.MyActivity) [mypackage.MyApplication_HiltComponents.SingletonC ? mypackage.MyApplication_HiltComponents.ActivityRetainedC ? mypackage.MyApplication_HiltComponents.ActivityC]
The following other entry points also depend on it:
dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.ViewModelFactoriesEntryPoint.getHiltViewModelMap() [mypackage.MyApplication_HiltComponents.SingletonC ? mypackage.MyApplication_HiltComponents.ActivityRetainedC ? mypackage.MyApplication_HiltComponents.ViewModelC]
请问如何在该单例Provide方法中获取所需的Intent参数?
核心矛盾在于:单例属于SingletonComponent(应用生命周期),而Activity Context和Intent属于ActivityComponent(页面生命周期),两者生命周期不匹配,直接在单例的Provide方法中依赖Activity Context会导致Dagger无法找到合适的绑定。下面提供三种可行方案:
方案一:延迟参数注入(推荐)
让MyClass先以无参数方式注入,然后在Activity中拿到Intent参数后再传递给MyClass完成初始化。
- 修改
MyClass,添加初始化方法:
class MyClass @Inject constructor( private val appContext: Context ) { private lateinit var myParam: String // 添加初始化方法,接收从Intent获取的参数 fun init(myParam: String) { this.myParam = myParam // 这里执行需要参数的初始化逻辑 } // 其他业务方法 }
- 保持
MyModule原有逻辑(仅依赖ApplicationContext):
@Module @InstallIn(SingletonComponent::class) class MyModule { @Provides @Singleton fun provideMyClass(@ApplicationContext appContext: Context): MyClass{ return MyClass(appContext) } }
- 在Activity中注入
MyClass后,立即传入Intent参数:
@AndroidEntryPoint class MyActivity : ComponentActivity() { @Inject lateinit var myClass: MyClass override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 从Intent中获取参数,可根据业务添加默认值或异常处理 val myParam = intent.getStringExtra("foo") ?: throw IllegalArgumentException("参数缺失") // 初始化MyClass myClass.init(myParam) } private fun myFunction() { doStuff(myClass) } }
这种方式既保留了MyClass的全局单例特性,又解决了生命周期不匹配的问题,是最符合Hilt设计理念的方案。
方案二:使用Assisted Injection(辅助注入)
如果必须在MyClass构造时就传入参数,可以用Hilt的Assisted Injection特性,结合手动参数传递实现。
- 添加Assisted Injection依赖(项目未引入时):
implementation "com.google.dagger:hilt-android:2.48" kapt "com.google.dagger:hilt-compiler:2.48" implementation "androidx.hilt:hilt-assisted:1.1.0" kapt "androidx.hilt:hilt-compiler:1.1.0"
- 修改
MyClass,用@Assisted标注动态参数:
class MyClass @AssistedInject constructor( private val appContext: Context, @Assisted private val myParam: String ) { // 业务逻辑 // 定义工厂接口 @AssistedFactory interface Factory { fun create(myParam: String): MyClass } }
- 修改
MyModule,绑定工厂实现:
@Module @InstallIn(SingletonComponent::class) class MyModule { @Provides @Singleton fun provideMyClassFactory(@ApplicationContext appContext: Context): MyClass.Factory { return object : MyClass.Factory { override fun create(myParam: String): MyClass { return MyClass(appContext, myParam) } } } }
- 在Activity中注入工厂并创建
MyClass实例(如需全局单例,可将实例存储到MyApplication或其他单例容器):
@AndroidEntryPoint class MyActivity : ComponentActivity() { @Inject lateinit var myClassFactory: MyClass.Factory private lateinit var myClass: MyClass override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val myParam = intent.getStringExtra("foo") ?: throw IllegalArgumentException("参数缺失") // 创建实例,如需全局单例可在此做缓存处理 myClass = myClassFactory.create(myParam) } private fun myFunction() { doStuff(myClass) } }
方案三:调整组件生命周期(不推荐)
如果MyClass不需要严格的全局单例,仅需在Activity生命周期内保持单例,可以将Module的作用域改为ActivityComponent:
- 修改
MyModule:
@Module @InstallIn(ActivityComponent::class) class MyModule { @Provides @ActivityScoped fun provideMyClass( @ActivityContext activityContext: Context ): MyClass{ val myParam = (activityContext as ComponentActivity).intent.getStringExtra("foo") ?: "" return MyClass(activityContext, myParam) } }
- 给
MyClass添加@ActivityScoped注解:
@ActivityScoped class MyClass @Inject constructor( private val context: Context, private val myParam: String ) { // 业务逻辑 }
这种方式的缺点是MyClass不再是全局单例,会随Activity销毁而销毁,每个Activity实例都会创建新的MyClass,不适合需要全局唯一实例的业务场景。
内容的提问来源于stack exchange,提问作者ocramot

