Android: 在 attachBaseContext 中使用 DataStore
Android: use DataStore in attachBaseContext
当我在我的应用程序中使用Datastore制作可以动态切换语言的功能时,我发现了一个错误:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
我的代码很简单:
open class BaseActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context) {
lifecycleScope.launch(Dispatchers.Main) {
var context = newBase
val language = withContext(Dispatchers.IO) {
DataStore.read(context, DataStore.KEY_LANG)
}
context = Language.changeLanguage(context, language ?: Language.ZH)
super.attachBaseContext(context)
}
}
}
Language.changeLanguage 将 return 更新本地的上下文:
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
fun changeLanguage(context: Context, lang: String): Context {
val configuration = context.resources.configuration
configuration.setLocale(getLocal(lang))
return context.createConfigurationContext(configuration)
}
我调试了我的应用程序,发现代码无法到达 super.attachBaseContext(context)
并完成。
我不知道原因,但我猜是因为协程。
问题是 Application
是一个 ContextWrapper
并且在 attachBaseContext
returns 之后假定基础上下文已经正确设置。
由于您将调用 super.attachBaseContext
推迟到协同程序的末尾,因此在协同程序有机会 运行 之前不会调用此方法(因为发生在 Dispatchers.Main
这可能会在正常初始化完成之后,因为这是同步发生的),打破了方法的契约。
实际上你有三个选择:
- 接受您必须阻塞才能同步获取更新的上下文。虽然不理想,但无需更改其他代码即可工作。
- 创建和管理您自己的用于在下游创建资源的上下文。可能是最不方便的,因为很多库都使用
context.getApplicationContext()
.
- 使用其他人创建的库来解决问题,因为它可能不是唯一的。 Google 迅速将我指向 YarikSOffice / lingver
当我在我的应用程序中使用Datastore制作可以动态切换语言的功能时,我发现了一个错误:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference
我的代码很简单:
open class BaseActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context) {
lifecycleScope.launch(Dispatchers.Main) {
var context = newBase
val language = withContext(Dispatchers.IO) {
DataStore.read(context, DataStore.KEY_LANG)
}
context = Language.changeLanguage(context, language ?: Language.ZH)
super.attachBaseContext(context)
}
}
}
Language.changeLanguage 将 return 更新本地的上下文:
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
fun changeLanguage(context: Context, lang: String): Context {
val configuration = context.resources.configuration
configuration.setLocale(getLocal(lang))
return context.createConfigurationContext(configuration)
}
我调试了我的应用程序,发现代码无法到达 super.attachBaseContext(context)
并完成。
我不知道原因,但我猜是因为协程。
问题是 Application
是一个 ContextWrapper
并且在 attachBaseContext
returns 之后假定基础上下文已经正确设置。
由于您将调用 super.attachBaseContext
推迟到协同程序的末尾,因此在协同程序有机会 运行 之前不会调用此方法(因为发生在 Dispatchers.Main
这可能会在正常初始化完成之后,因为这是同步发生的),打破了方法的契约。
实际上你有三个选择:
- 接受您必须阻塞才能同步获取更新的上下文。虽然不理想,但无需更改其他代码即可工作。
- 创建和管理您自己的用于在下游创建资源的上下文。可能是最不方便的,因为很多库都使用
context.getApplicationContext()
. - 使用其他人创建的库来解决问题,因为它可能不是唯一的。 Google 迅速将我指向 YarikSOffice / lingver