简体   繁体   中英

ContributesAndroidInjector Module must be set

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:

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();
  }

Maybe i don't understand something? Thanks!

好的,经过官方dagger repo的一些研究和帮助,我找到了解决方案:所有子组件抽象模块必须仅提供静态@Provides方法,或者仅使用构造函数注入,因为无法设置模块实例。

I had the same error and after research I found mistake import module Import was like this

import com.google.android.datatransport.runtime.dagger.Module

but should be

import dagger.Module

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM