@ContributesAndroidInjector: strange code generation and error - Module must be set
See original GitHub issueHi! I’m trying to inject activity dependencies into fragment presenter. App structure is very simple.
TabsActivity - viewpager – TabFragment - page item – […]
TabsActivity shows view TabsActivityModule - provides FragmentManager (for example) and some stuff for TabsActivity TabFragmentModule provides stuff for TabFragment. Every TabFragment has own Presenter. Presenter injects TabActivityModule dependencies. In my case - Presenter injects FragmentManager
Here is code:
Application class
class App extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> dispatchingAndroidInjector;
@Override
public AndroidInjector<Activity> activityInjector() {
return dispatchingAndroidInjector;
}
/*
blah blah creating app component onCreate() ...
DaggerAppComponent
.builder()
.context(this)
.build()
.inject(this);
*/
}
Root application component
@Singleton
@Component(modules = {InjectorsModule.class})
public interface AppComponent {
@Component.Builder
interface Builder {
@BindsInstance
Builder context(Context context);
AppComponent build();
}
void inject(App app);
}
Here is android views contributors InjectorsModule
@Module(includes = {AndroidSupportInjectionModule.class})
public abstract class InjectorsModule {
@ActivityScope
@ContributesAndroidInjector(modules = {TabsActivityModule.class})
abstract TabsActivity tabsActivityInjector();
}
Activity TabsActivityModule
@Module
public abstract class TabsActivityModule {
@Provides
@ActivityScope
public FragmentManager provideFragmentManager(TabsActivity activity) {
return activity.getFragmentManager();
}
@Provides @Named("activity")
@ActivityScope
public Context provideActivityContext(TabsActivity activity) {
return activity;
}
// Here contributes tab fragment
@FragmentScope
@ContributesAndroidInjector(modules = {TabFragmentModule.class})
public abstract TabFragment tabFragment();
}
Fragment TabFragmentModule
@Module
public abstract class TabFragmentModule {
@Provides @Named("answer")
@FragmentScope
public int provideSomeInt() {
return 42;
}
}
And last, just for example - TabPresenter
public class TabPresenter {
@Inject @Named("answer") int mAnswer;
@Inject @Named("activity") mActivityContext;
@Inject FragmentManager mFragmentManager;
@Inject
public TabPresenter() {}
public void doNothingWithAnswerAndContextAndFragmentManager() {
mAnswer *= 1;
}
}
After this all, build finishes with success status. But at runtime, i’m getting InvalidStateException immediately while fragment injects: fragment
onAttach(Activity activity) {
AndroidInjection.inject(this);
super.onAttach(activity);
}
Exception stacktrace:
FATAL EXCEPTION: main
Process: com.example.drive.dagger2new, PID: 3064
java.lang.RuntimeException:
Unable to start activity
ComponentInfo{com.example.drive.dagger2new/com.example.ui.TabsActivity}:
java.lang.IllegalStateException:
com.example.dagger.modules.main.TabsActivityModule must be set
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2680)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2741)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1488)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6169)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:778)
Caused by: java.lang.IllegalStateException: com.example.dagger.modules.main.TabsActivityModule must be set
at com.example.dagger.components.DaggerAppComponent$TabsActivitySubcomponentBuilder.build(DaggerAppComponent.java:116)
at com.example.dagger.components.DaggerAppComponent$TabsActivitySubcomponentBuilder.build(DaggerAppComponent.java:106)
at dagger.android.AndroidInjector$Builder.create(AndroidInjector.java:68)
at dagger.android.DispatchingAndroidInjector.maybeInject(DispatchingAndroidInjector.java:79)
at dagger.android.DispatchingAndroidInjector.inject(DispatchingAndroidInjector.java:104)
at dagger.android.AndroidInjection.inject(AndroidInjection.java:61)
at com.example.base.BaseActivity.onCreate(BaseActivity.java:35)
at com.example.ui.TabsActivity.onCreate(TabsActivity.java:13)
at android.app.Activity.performCreate(Activity.java:6679)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2633)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2741)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1488)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6169)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:778)
After looking at this, i though, that i just forget to set some new module instance. But, I don’t doing this, because @ContributesAndroidInjector do it for me. Ok, when i look at generated code, i saw that TabsActivitySubcomponentBuilder does not have a setter for TabsActivityModule.
private final class TabsActivitySubcomponentBuilder
extends InjectorsModule_TabsActivityInjector.TabsActivitySubcomponent.Builder {
private TabsActivityModule tabsActivityModule;
private TabsActivity seedInstance;
@Override
public InjectorsModule_TabsActivityInjector.TabsActivitySubcomponent build() {
if (tabsActivityModule == null) {
// Exceptions throws here
throw new IllegalStateException(
TabsActivityModule.class.getCanonicalName() + " must be set");
}
if (seedInstance == null) {
throw new IllegalStateException(TabsActivity.class.getCanonicalName() + " must be set");
}
return new TabsActivitySubcomponentImpl(this);
}
// here we seeing setter for activity
@Override
public void seedInstance(TabsActivity arg0) {
this.seedInstance = Preconditions.checkNotNull(arg0);
}
// and for module??
}
And finally, AndroidInjector.Builder sets only activity instance:
abstract class Builder<T> implements AndroidInjector.Factory<T> {
@Override
public final AndroidInjector<T> create(T instance) {
// here
seedInstance(instance);
// and here must be seedModule(module), but nothing
return build();
}
/**
* Provides {@code instance} to be used in the binding graph of the built {@link
* AndroidInjector}. By default, this is used as a {@link BindsInstance} method, but it may be
* overridden to provide any modules which need a reference to the activity.
*
* <p>This should be the same instance that will be passed to {@link #inject(Object)}.
*/
@BindsInstance
public abstract void seedInstance(T instance);
/** Returns a newly-constructed {@link AndroidInjector}. */
public abstract AndroidInjector<T> build();
}
Sorry for so long answer, maybe i don’t understand something? Thanks!
Issue Analytics
- State:
- Created 6 years ago
- Comments:5
Top GitHub Comments
Ah, there’s an issue for this already. You can fix your code by making your
@Provides
methods static.@ronshapiro Works like magic! Thank you!