Dariush Fathi
Dariush Fathi

Reputation: 1695

Hilt can not cast ContextImpl to Application

I use Hilt and recently had a strange problem. For a limited number of devices, I encounter the following error.

*dagger.hilt.android.internal.modules.ApplicationContextModule.provideApplication
ApplicationContextModule.java, line 45
java.lang.ClassCastException: android.app.ContextImpl cannot be cast to 
android.app.Application*

ApplicationContextModule generated by Hilt:

/** Provides a binding for an Android BinderFragment Context. */
@Module
@InstallIn(ApplicationComponent.class)
public final class ApplicationContextModule {
  private final Context applicationContext;

  public ApplicationContextModule(Context applicationContext) {
    this.applicationContext = applicationContext;
  }

  @Provides
  @ApplicationContext
  Context provideContext() {
    return applicationContext;
  }

  @Provides
  Application provideApplication() {
    return (Application) applicationContext.getApplicationContext();
  }
}

NOTICE: ApplicationContextModule was generated by Hilt

@HiltAndroidApp
class MyApp : Application(), Configuration.Provider {
   ...
}

What I am doing is just create an instance of RootWarninginViewModel (which is annotated with @ViewModelInject)

class RootWarningViewModel @ViewModelInject constructor(
   ...
) : ViewModel() {

This is what I am actually doing:

@AndroidEntryPoint
class RootWarningActivity : AppCompatActivity() {

    private val viewModel: RootWarningViewModel by viewModels()

}

And here is the complete crash log:

***dagger.hilt.android.internal.modules.ApplicationContextModule.provideApplication ApplicationContextModule.java:45
dagger.hilt.android.internal.modules.ApplicationContextModule_ProvideApplicationFactory.provideApplication ApplicationContextModule_ProvideApplicationFactory.java:34
*.app.DaggerMyApp_HiltComponents_SingletonC$ActivityRetainedCImpl$ActivityCImpl.provideFactory DaggerMyApp_HiltComponents_SingletonC.java:6658
*.app.DaggerMyApp_HiltComponents_SingletonC$ActivityRetainedCImpl$ActivityCImpl.getActivityViewModelFactory DaggerMyApp_HiltComponents_SingletonC.java:6663
dagger.hilt.android.internal.lifecycle.DefaultViewModelFactories.getActivityFactory DefaultViewModelFactories.java:50
*.ui.splash.Hilt_RootWarningActivity.getDefaultViewModelProviderFactory Hilt_RootWarningActivity.java:61
-$$LambdaGroup$ks$bK8-QGfmFwPkSFGulNZ22qySm7s.a -.java:28
androidx.lifecycle.ViewModelLazy.getValue ViewModelLazy.java:52
androidx.lifecycle.ViewModelLazy.getValue ViewModelLazy.java:41
*.ui.splash.RootWarningActivity.getViewModel RootWarningActivity.java
*.ui.splash.RootWarningActivity.getViewModel RootWarningActivity.java:11
*.ui.base.BaseActivity.onCreate BaseActivity.java:74
*.ui.splash.Hilt_RootWarningActivity.onCreate Hilt_RootWarningActivity.java:31***

Upvotes: 2

Views: 4306

Answers (3)

Amjad Alwareh
Amjad Alwareh

Reputation: 3261

As I understood from the question, you want the ApplicationContext, right?
Hilt provides a way to get the Context by calling

@ApplicationContext context: Context

I used it with lots of APIs and devices and never had any issues.

Upvotes: 0

Andriy D.
Andriy D.

Reputation: 1987

it's a known bug and the issue in GitHub is closed already but the app will still crash if the Application class overrides getApplicationContext() like below:

@HiltAndroidApp
class App : Application() {

override fun getApplicationContext(): Context {
    return super.getApplicationContext().updateContextResources()
}

in my case the problem was in updateContextResources() function

fun Context.updateContextResources(): Context {
    Locale.setDefault(localeFromPreferences)
    val locale = Locale.getDefault()

    if (currentLocale == locale && this is Application) {
        return this
    }

    val resources = resources
    val configuration = resources.configuration
    configuration.setCurrentLocale(locale)

    configuration.setLayoutDirection(locale)

    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
       createConfigurationContext(configuration)
    } else {
        @Suppress("DEPRECATION")
        resources.updateConfiguration(configuration, resources.displayMetrics)
        this
    }
}

createConfigurationContext(configuration) returns ContextImpl instead of Application, this cause the crash.

Here is my hotfix(new context will be created only when the current one is not Application):

return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && this !is Application) {
    createConfigurationContext(configuration)
} else {
    @Suppress("DEPRECATION")
    resources.updateConfiguration(configuration, resources.displayMetrics)
    this
}

Upvotes: 3

Nikola Despotoski
Nikola Despotoski

Reputation: 50538

You should remove the argument in the constructor of the module and request the application Context to be provided by hilt itself. So you can safely cast it to Application. Maybe some of the manufacturers return different context when you call getApplicationContext() on the actual application context.

@Module
@InstallIn(ApplicationComponent.class)
public final class ApplicationContextModule {

 @Provides
  Application provideApplication(@ApplicationContext application: Context) {
    return (Application) application;
  }
}

ApplicationComponent is already seeded with application context. You don't have to provide functions for that dependency.

Upvotes: 0

Related Questions