Dagger-Hilt注入Activity上下文至ViewModel依赖类失败求助
问题分析与解决方案
错误根源
- SingletonComponent中注入@ActivityContext:SingletonComponent是应用生命周期级组件,不存在关联的Activity实例,Dagger无法提供
@ActivityContext注解的上下文。 - ActivityComponent的作用域限制:若AppLanguageProvider为ActivityScoped,仅能在Activity或其子组件(如Fragment)中注入;若在ViewModel、单例类等非Activity组件中注入,Dagger会因找不到绑定关系报错。
- 强转AppContext为Activity:Application Context与Activity Context是完全不同的实例,强转会直接抛出
ClassCastException,属于错误用法。
正确实现方案
1. 重构AppLanguageProvider,基于AppContext+SharedPreferences
不再依赖ActivityContext设置Locale,而是将用户选择的语言偏好存入SharedPreferences,后续通过BaseActivity统一应用配置。
interface AppLanguageProvider { fun getAppLanguage(): Language fun setAppLanguage(languageCode: String?) } @Singleton class AppLanguageProviderImpl @Inject constructor( @ApplicationContext private val context: Context, private val sharedPreferences: SharedPreferences ) : AppLanguageProvider { private companion object { const val KEY_SELECTED_LANGUAGE = "selected_language" } override fun getAppLanguage(): Language { // 优先取用户保存的语言,无则用系统默认 val savedLanguageCode = sharedPreferences.getString(KEY_SELECTED_LANGUAGE, null) val locale = savedLanguageCode?.let { Locale(it) } ?: context.resources.configuration.locale return when (locale.language) { "tr" -> Language.Turkish "en" -> Language.English else -> Language.Unknown } } override fun setAppLanguage(languageCode: String?) { sharedPreferences.edit() .putString(KEY_SELECTED_LANGUAGE, languageCode) .apply() } }
2. 在AppModule中提供实例
@Module @InstallIn(SingletonComponent::class) object AppModule { @Provides @Singleton fun provideAppLanguageProvider( @ApplicationContext context: Context, sharedPreferences: SharedPreferences ): AppLanguageProvider = AppLanguageProviderImpl(context, sharedPreferences) @Provides @Singleton fun provideSharedPreferences(@ApplicationContext context: Context): SharedPreferences { return context.getSharedPreferences("app_preferences", Context.MODE_PRIVATE) } }
3. 创建BaseActivity统一应用Locale
所有Activity继承此类,确保启动时加载正确的语言配置:
abstract class BaseActivity : AppCompatActivity() { @Inject lateinit var languageProvider: AppLanguageProvider override fun attachBaseContext(newBase: Context?) { super.attachBaseContext(updateLocale(newBase)) } private fun updateLocale(context: Context?): Context? { val languageCode = when (languageProvider.getAppLanguage()) { Language.Turkish -> "tr" Language.English -> "en" else -> null } if (languageCode.isNullOrEmpty()) return context val locale = Locale(languageCode) Locale.setDefault(locale) val config = context?.resources?.configuration?.apply { setLocale(locale) setLayoutDirection(locale) } return config?.let { context.createConfigurationContext(it) } ?: context } }
4. 切换语言后重启Activity生效
用户选择语言后,调用setAppLanguage并重启页面:
// 示例:设置页面的语言保存逻辑 btnSaveLanguage.setOnClickListener { val selectedCode = if (rbTurkish.isChecked) "tr" else "en" languageProvider.setAppLanguage(selectedCode) // 重启当前Activity使配置生效 finish() startActivity(intent) }
额外说明
- 从Android 7.0(API 24)开始,
updateConfiguration已被废弃,推荐使用createConfigurationContext实现配置更新,上述代码已兼容该规范。 - 确保所有业务Activity都继承BaseActivity,才能保证语言切换在全局生效。
- ViewModel或单例类只需注入
AppLanguageProvider即可获取当前语言,无需依赖ActivityContext。
内容的提问来源于stack exchange,提问作者Sevban Bayır
相关产品推荐
相关产品推荐

