psycongroo
psycongroo

Reputation: 53

Android: use DataStore in attachBaseContext

When I use Datastore to make the function that can switch language dynamically in my App, I found a error :

java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.pm.ApplicationInfo android.content.Context.getApplicationInfo()' on a null object reference

my code is easy:

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 will return a context that update the local:

    @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)

}

I debug my App and found that the code cant reach super.attachBaseContext(context) and finished. I dont know the reason but i guess that because the coroutines.

Upvotes: 4

Views: 1571

Answers (1)

Kiskae
Kiskae

Reputation: 25603

The problem is that Application is a ContextWrapper and after attachBaseContext returns it presumes the underlying context has been properly set.

Since you defer calling super.attachBaseContext to the end of the coroutine this method will not be called until the coroutine gets the chance to run (because happens on Dispatchers.Main this would probably be after normal initialization is done since that happens synchronously), breaking the contract of the method.

Practically you have three options:

  1. accept that you will have to block to synchronously obtain an updated context. While not ideal this would work without changing other code.
  2. create and manage your own context that you use to create resources downstream. Probably the most inconvenient since a lot of libraries use context.getApplicationContext().
  3. Use a library that someone else has created to solve the problem, since it is probably not unique. Google quickly pointed me at YarikSOffice / lingver

Upvotes: 1

Related Questions