You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dagger-Hilt注入Activity上下文至ViewModel依赖类失败求助

问题分析与解决方案

错误根源

  1. SingletonComponent中注入@ActivityContext:SingletonComponent是应用生命周期级组件,不存在关联的Activity实例,Dagger无法提供@ActivityContext注解的上下文。
  2. ActivityComponent的作用域限制:若AppLanguageProvider为ActivityScoped,仅能在Activity或其子组件(如Fragment)中注入;若在ViewModel、单例类等非Activity组件中注入,Dagger会因找不到绑定关系报错。
  3. 强转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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 04:00:41