RuntimeException with Dagger 2 on Android 7.0 and Samsung devices
Finally I found a way to resolve the crashes caused by using Dagger 2 under Android 7.0 for my application. Please note that this does not resolve the issue with a custom application not being properly used under Android 7.0. In my case I did not have important logic in my custom Application besides getting Dagger 2 implemented and so I just replaced the DaggerApplication
based implementation with the ApplicationlessInjection
below.
Known issues
- No dependency injection in custom application classes (probably this isn't a good idea with the freaking Android 7.0 OEM implementations anyway)
- Not all Dagger components where modified by me, I replaced only
DaggerAppCompatActivity
,DaggerIntentService
andDaggerFragment
. If you are using other components likeDaggerDialogFragment
orDaggerBroadcastReceiver
you need to create your own implements but I guess that should not be too hard :)
Implementation
Stop using DaggerApplication
. Either extend your custom application again from the standard Application
or get rid of the custom application entirely. For the dependency injection with Dagger 2 its not needed anymore. Just extend e.g. FixedDaggerAppCompatActivity
and you are good to go with the Dagger 2 DI for activities.
You may notice that I am still passing the application context to the ApplicationlessInjection.getInstance()
. The dependency injection itself does not need the context at all but I want to be able to easily inject the application context into my other components and modules. And there I do not care if the application context is my custom App or some crazy other stuff from Android 7.0 as long as it is a context.
ApplicationlessInjection
public class ApplicationlessInjection implements HasActivityInjector, HasFragmentInjector, HasSupportFragmentInjector, HasServiceInjector, HasBroadcastReceiverInjector, HasContentProviderInjector { private static ApplicationlessInjection instance = null; @Inject DispatchingAndroidInjector<Activity> activityInjector; @Inject DispatchingAndroidInjector<BroadcastReceiver> broadcastReceiverInjector; @Inject DispatchingAndroidInjector<android.app.Fragment> fragmentInjector; @Inject DispatchingAndroidInjector<Fragment> supportFragmentInjector; @Inject DispatchingAndroidInjector<Service> serviceInjector; @Inject DispatchingAndroidInjector<ContentProvider> contentProviderInjector; public ApplicationlessInjection(Context applicationContext) { AppComponent appComponent = DaggerAppComponent.builder().context(applicationContext).build(); appComponent.inject(this); } @Override public DispatchingAndroidInjector<Activity> activityInjector() { return activityInjector; } @Override public DispatchingAndroidInjector<android.app.Fragment> fragmentInjector() { return fragmentInjector; } @Override public DispatchingAndroidInjector<Fragment> supportFragmentInjector() { return supportFragmentInjector; } @Override public DispatchingAndroidInjector<BroadcastReceiver> broadcastReceiverInjector() { return broadcastReceiverInjector; } @Override public DispatchingAndroidInjector<Service> serviceInjector() { return serviceInjector; } @Override public AndroidInjector<ContentProvider> contentProviderInjector() { return contentProviderInjector; } public static ApplicationlessInjection getInstance(Context applicationContext) { if(instance == null) { synchronized(ApplicationlessInjection.class) { if (instance == null) { instance = new ApplicationlessInjection(applicationContext); } } } return instance; }}
FixedDaggerAppCompatActivity
public abstract class FixedDaggerAppCompatActivity extends AppCompatActivity implements HasFragmentInjector, HasSupportFragmentInjector { @Inject DispatchingAndroidInjector<Fragment> supportFragmentInjector; @Inject DispatchingAndroidInjector<android.app.Fragment> frameworkFragmentInjector; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { inject(); super.onCreate(savedInstanceState); } @Override public AndroidInjector<Fragment> supportFragmentInjector() { return supportFragmentInjector; } @Override public AndroidInjector<android.app.Fragment> fragmentInjector() { return frameworkFragmentInjector; } private void inject() { ApplicationlessInjection injection = ApplicationlessInjection.getInstance(getApplicationContext()); AndroidInjector<Activity> activityInjector = injection.activityInjector(); if (activityInjector == null) { throw new NullPointerException("ApplicationlessInjection.activityInjector() returned null"); } activityInjector.inject(this); }}
FixedDaggerFragment
public abstract class FixedDaggerFragment extends Fragment implements HasSupportFragmentInjector { @Inject DispatchingAndroidInjector<Fragment> childFragmentInjector; @Override public void onAttach(Context context) { inject(); super.onAttach(context); } @Override public AndroidInjector<Fragment> supportFragmentInjector() { return childFragmentInjector; } public void inject() { HasSupportFragmentInjector hasSupportFragmentInjector = findHasFragmentInjector(); AndroidInjector<Fragment> fragmentInjector = hasSupportFragmentInjector.supportFragmentInjector(); if (fragmentInjector == null) { throw new NullPointerException(String.format("%s.supportFragmentInjector() returned null", hasSupportFragmentInjector.getClass().getCanonicalName())); } fragmentInjector.inject(this); } private HasSupportFragmentInjector findHasFragmentInjector() { Fragment parentFragment = this; while ((parentFragment = parentFragment.getParentFragment()) != null) { if (parentFragment instanceof HasSupportFragmentInjector) { return (HasSupportFragmentInjector) parentFragment; } } Activity activity = getActivity(); if (activity instanceof HasSupportFragmentInjector) { return (HasSupportFragmentInjector) activity; } ApplicationlessInjection injection = ApplicationlessInjection.getInstance(activity.getApplicationContext()); if (injection != null) { return injection; } throw new IllegalArgumentException(String.format("No injector was found for %s", getClass().getCanonicalName())); }}
FixedDaggerIntentService
public abstract class FixedDaggerIntentService extends IntentService { public FixedDaggerIntentService(String name) { super(name); } @Override public void onCreate() { inject(); super.onCreate(); } private void inject() { ApplicationlessInjection injection = ApplicationlessInjection.getInstance(getApplicationContext()); AndroidInjector<Service> serviceInjector = injection.serviceInjector(); if (serviceInjector == null) { throw new NullPointerException("ApplicationlessInjection.serviceInjector() returned null"); } serviceInjector.inject(this); }}
My AppComponent
@Singleton@Component(modules = { AppModule.class, ActivityBindingModule.class, AndroidSupportInjectionModule.class})public interface AppComponent extends AndroidInjector<ApplicationlessInjection> { @Override void inject(ApplicationlessInjection instance); @Component.Builder interface Builder { @BindsInstance AppComponent.Builder context(Context applicationContext); AppComponent build(); }}
My AppModule
@Modulepublic abstract class AppModule { @Binds @ApplicationContext abstract Context bindContext(Context applicationContext);}
And for the sake of completeness my @ApplicationContext annotation
@Qualifier@Retention(RetentionPolicy.RUNTIME)public @interface ApplicationContext {}
Hopefully I can help someone else with my code as well. For me I could resolve all crashes related to introducing Dagger 2 and the weird Android 7.0 versions.
If more clarification is needed just let me know!
I encountered the same problem in my app and i resolved it by using below code :
Application app = activity.getApplication();if(app == null) { app = (Application)activity.getApplicationContext();}