Home » Android » android – Dagger v2: Inject 2 different scopes into one object

android – Dagger v2: Inject 2 different scopes into one object

Posted by: admin May 14, 2020 Leave a comment

Questions:

I have moduleA setup as an application wide singleton provider, ModuleB as a user related object provider

My user display fragment will use system wide bus to send message to others and use user related object to display.

Problem cannot inject different scrope class into one object. Use component.getX method works fine, but inject is prefered way.
Error message:
@UserScope may not reference bindings with difference scopes: @Provides @Singleton Bus ModuleA.provideBus()

@Module
public class ModuleA {
  @Provides @Singleton Bus provideBus() {...}
}

Module B as user related Info provider

@Module
public class ModuleB{
  private final User user;
  public ModuleB(User user) {...}
  @Provides @UserScope User provideUser() {}
  @Provides @UserScope UserManager provideUserManager() {}
}

Components setup like following:

@Component (modules={ModuleA.class})
@Singleton
public interface ComponentA {
  Bus getBus();
  void inject(ClassA target);
}

@Component(modules={ModuleB.class})
@UserScope
public interface ComponentB {
  User getUser();
  UserManager getUserManager();
  void inject(ClassA target);
}


class UserFragment exrtends Fragment {
   @Inject Bus bus;
   @Inject UserManager userManager;
   public void onCreate() {
      getComponentA().inject(this);
      getComponentB().inject(this);
   }
}
How to&Answers:

Try this configuration, it works for me. There is really a lack of good documentation about Dagger2 so I studied a few open-source examples of code that you can find in GitHub etc by keyword like Dagger2.

Application level Component

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
    // exported for child-components
    Bus eventBus();
}

Application level Module

@Module
public class AppModule {
    @Provides @Singleton
    Bus provideBus() {
        return BusProvider.getInstance();
    }
}

Activity level Component

@ActivityScope
@Component(dependencies=AppComponent.class, modules=MainActivityModule.class)
public interface MainActivityComponent {
    void inject( MainActivity mainActivity );
}

Activity level Module

@Module
public class MainActivityModule {
    private final MainActivity mActivity;

    public MainActivityModule( MainActivity activity ) {
        mActivity = activity;
    }

    @Provides
    MainActivityTitleController provideTitleController() {
        return new MainActivityTitleController( mActivity );
    }
}

Android Application class

public class MyApplication extends Application {
    private AppComponent mAppComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        // Dagger2
        mAppComponent = Dagger_AppComponent.builder()
            .appModule( new AppModule( this ))
            .build();
    }

    public AppComponent getComponent() {
        return mAppComponent;
    }

    public static AppComponent getComponent( Context context ) {
        return ((MyApplication)context.getApplicationContext()).getComponent();
    }
}

And finally Activity

public class MainActivity extends ActionBarActivity {

    // Injectable fields
    @Inject Bus mEventBus;
    @Inject MainActivityTitleController mTitleController;

    private MainActivityComponent mComponent;

    @Override
    protected void onCreate( Bundle savedInstanceState ) {
        // Dagger2
        mComponent = Dagger_MainActivityComponent.builder()
            .appComponent( ((MyApplication)getApplication()).getComponent() )
            .mainActivityModule( new MainActivityModule( this ) )
            .build();
        mComponent.inject( this );
    }
}

Answer:

I think the main problem in your code snippets you provided, is that your ModuleB should have a dependency on ModuleA to correctly provide the singleton with the error you were getting. I.e. this should work:

@Component(modules={ModuleB.class}, dependencies = ComponentA.class)
@UserScope
public interface ComponentB {
    User getUser();
    UserManager getUserManager();
    void inject(MainActivity target);
}

I recreated your classes and made some assumptions to fill in the blanks, and it seems to work fine. You can see the full working code here on GitHub. The only difference in my code is, what you called ClassA/UserFragment I just called MainActivity but otherwise the structure is the same.