【问题标题】:Android application context configuration doesn't gets updated with the new LocaleAndroid 应用程序上下文配置不会使用新的语言环境进行更新
【发布时间】:2019-03-12 20:38:02
【问题描述】:

当我不使用ViewModel 中的Context 相关类(活动、片段、服务、视图等)或保留所有业务逻辑的自定义构建类时,我需要访问应用程序的本地化字符串。例如,在LiveData 的帮助下将String 发布到UI

toastMessageLiveData.value = buildLastLoginToastMessage()

fun buildLastLoginToastMessage() {
   val lastLoginDiff = (System.currentMillis() - getLastLogin()) / DateUtils.HOURS_IN_MILLIS

   return MyApp.instance.getString(R.string.last_login_txt, lastLoginDiff)
}

现在,在 Activity 中,我正在观察 LiveData 的变化,并显示 Toast 消息。

这很好用,但是如果用户更改应用程序的语言就会出现问题。布局已更新,我可以看到使用了新语言环境的语言,但 MyApp.instance.getString(R.string....) 函数仍在使用旧语言环境。如果我强制终止应用程序并重新启动它,它会工作,因为再次调用应用程序的 attachBaseContext 并应用新的区域设置。但是,我不想强​​行终止应用程序,我需要一个解决方案来更新应用程序的配置。

创建或更新Context的实用函数

fun buildLocalizedContext(context: Context): Context {
   // From preferences, load the saved Language and Country codes
   val language = getSavedLanguage()
   val country = getSavedCountry()

   // Create the new locale
   val locale = Locale(language, country)
   Locale.setDefault(locale)

   val resources = context.resources
   val configuration = Configuration(resources.configuration)

   // Create a new configration or update the existing one if the API is less than 17
   if (Build.VERSION.SDK_INT >= 17) {
      configuration.setLocale(locale)
      return context.createConfigurationContext(configuration)
   } else {
      configuration.locale = locale
      resources.updateConfiguration(configuration, resources.getDisplayMetrics())
   }

   return context
}

当应用启动时,应用新的语言环境

class MyApp: Application {
    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(LocaleHelper.instance.buildLocalizedContext(base))
    }
}

其他活动正在使用的BaseActivity 类:

class BaseActivity: AppCompatActivity {
    override fun attachBaseContext(newBase: Context) {
        super.attachBaseContext(LocaleHelper.instance.buildLocalizedContext(newBase))
    }
}

这是我到目前为止所做的,但这也不起作用:

fun changeLocale(locale: Locale, app: Application) {
    Locale.setDefault(locale)

    val resources = app.resources
    val configuration = resources.configuration

    if (Build.VERSION.SDK_INT >= 17) {
        configuration.setLocale(locale)
    }
    else {
        configuration.locale = locale
        resources.updateConfiguration(configuration, resources.displayMetrics)
    }
}

上面的代码 sn -p 不起作用,它不会改变应用程序的区域设置,只有当我强制重启应用程序时。

【问题讨论】:

  • “这很好用,但如果用户更改应用程序的语言,问题就来了”——应用程序并没有真正的语言。您的 setLocale() 方法一直有点像 hack,在 Android 中的支持参差不齐。话虽如此,是否有一些解决方案涉及您不直接访问Application,而是通过一些ContextWrapper,您可以在其中更改ContextWrapper 使用的语言环境?
  • @Commons - 一种解决方案是将字符串资源 id 发送到 Live Data 并从 ActivityFragment 获取字符串,但这通过移动业务打破了 MVVM 原则UI 的逻辑。在上面的例子中,你可以看到计算上次登录和构建字符串的业务逻辑是在ViewModel中进行的。在ViewModel 中调用MyApp.instance.getString(R.string....) 函数时会出现问题,因为只有在我重新启动应用程序时,MyApp.instance.configuration.locale 中的Locale 才会更新为新的语言环境。
  • 我的意思是,除了调用MyApp.instance.getString(),也许您可​​以使用除MyApp.instance 之外的其他东西(例如MyApp.instance 周围的ContextWrapper),它在范围内仍然是全局的,但可以在运行时替换。或者,也许字符串是通过调用函数创建的,调用者提供Activity
  • @CommonsWare - 感谢您的快速回复。如果你能给我一个例子,我如何在MyApp.instance 周围使用或包裹ContextWrapper,或者如何在不违反MVVM 原则的情况下从ViewModel 访问Activity
  • 关于后者,fun buildLastLoginToastMessage(ctxt: Context) 在您的上述代码 sn-p 中,该函数由您的活动或片段调用并传入活动,您使用 ctxt 作为您的 @ 987654361@电话。就前者而言,我只是建议您自己研究的途径。就个人而言,我永远不会编写使用 setLocale() 的应用程序。

标签: android locale


【解决方案1】:

正如您所经历的,语言和其他几种配置仅适用于视图上下文,而不是应用程序上下文。 你似乎处于一个奇怪的境地。您可能需要重新审视您的架构。通常,您希望获取视图中的字符串并将其传递给您的 ViewModel 进行处理。 但是,如果您想让它按原样工作,也许您可​​以将 WeakReference 存储到应用程序对象中的 View 并使用它来获取本地化字符串。你也可以保持对视图的硬引用,但是你必须确保清除它以避免内存泄漏。 最好的解决方案是修改您的架构,但这也是一个不错的解决方案。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        (application as App).activity = WeakReference<Activity>(this)
        }
}

class App : Application {
var activity : WeakReference<Activity>? = null
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-04
    • 1970-01-01
    • 1970-01-01
    • 2020-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-19
    相关资源
    最近更新 更多