Reputation: 1695
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
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
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
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