Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dagger 2, Library modules and @Singleton

I'm trying to use Dagger 2 in an Android Project that has several Android Library modules and I'd like to be able to provide singleton scoped instances of classes from these modules.

Currently I'm able to define Components inside the library modules and inject the instances in the main Application module.

What I'm not able to do is to provide an instance as singleton.

The project structure is the following:

Project
├── app
├── library1
·
·
·
└── libraryN

In the libraries I'm defining the components this way:

@Component
public interface LibraryComponent {

    // Provide instances of MyManager to MainComponent:
    MyManager getMyManager();
}

And MyManager looks like this:

public class MyManager {

    private static final String TAG = "MyManager";

    @Inject
    public MyManager() {
        Log.d(TAG, "Creating MyManager");
    }
}

In the main App I'm defining my component this way:

@ApplicationScope
@Component(dependencies = {LibraryComponent.class, Library2Component.class})
public interface MainComponent {

    void inject(MainActivity target);
}

This is the Application class:

public class App extends Application {
    private MainComponent component;

    @Override
    public void onCreate() {
        super.onCreate();

        component = DaggerMainComponent.builder()
                .libraryComponent(DaggerLibraryComponent.create())
                .library2Component(DaggerLibrary2Component.create())
                .build();
    }

    public MainComponent getComponent() {
        return component;
    }
}

If I add a scope to only one library component, then I'm able to provide the manager as a singleton. But if I try to do the same with one more library I'm getting the error:

@com.codeblast.dagger2lib.ApplicationScope com.codeblast.dagger2lib.MainComponent depends on more than one scoped component:
@Component(dependencies = {LibraryComponent.class, Library2Component.class})
^
      @com.codeblast.library.LibraryScope com.codeblast.library.LibraryComponent
      @com.codeblast.library2.Library2Scope com.codeblast.library2.Library2Component

Again, what I'd like to achieve is just to inject in my main Application project singleton scoped instances of some Managers provided by the Library projects.

like image 824
Roberto Leinardi Avatar asked Nov 08 '16 15:11

Roberto Leinardi


2 Answers

As suggested by @EpicPandaForce, using Dagger Modules instead of Components solved my issue.

Following, the necessary changes that I had to make.

The first one is deleting the library Components and creating library Modules:

@Module
public class LibraryModule {

    @Singleton
    @Provides
    MyManager provideMyManager(MyUtility myUtility) {
        return new MyManager(myUtility);
    }
}

Than just specify those Modules in the App Component, in place of the Component's dependencies:

@Singleton
@Component(modules = {LibraryModule.class, Library2Module.class})
public interface MainComponent {

    void inject(MainActivity target);
}

And that's it, whit this code the Manager classes, annotated with the @Singleton scope, are correctly instantiated only once.

like image 65
Roberto Leinardi Avatar answered Oct 16 '22 08:10

Roberto Leinardi


Try to unify the library components under a single component (eg.: AllLibrariesComponent), and then let your MainComponent have as dependency only the AllLibrariesComponent.

Library1:

@Component
@Singleton
public interface LibraryComponent {

    // Provide instances of MyManager to MainComponent:
    MyManager getMyManager();

}

@Singleton
public class MyManager {

    private static final String TAG = "MyManager";

    @Inject
    public MyManager() {
        Log.d(TAG, "*** Creating MyManager 1 ***");
    }
}

Library2:

@Singleton
@Component
public interface Library2Component {

    // Provide instances of MyManager to MainComponent:
    MyManager2 getManager2();

}

@Singleton
public class MyManager2 {

    private static final String TAG = "MyManager";

    @Inject
    public MyManager2() {
        Log.d(TAG, "*** Creating MyManager 2 *** ");
    }
}

app:

@Singleton
@Component
public interface AllLibrariesComponent extends Library2Component, LibraryComponent{
}

@PerApplication
@Component(dependencies = AllLibrariesComponent.class)
public interface MainComponent {

    void inject(MainActivity activity);

}

// .... 
// and the instantication in your application class:
mainComponent = DaggerMainComponent.builder()
                .allLibrariesComponent(DaggerAllLibrariesComponent.create())                  
                .build();
like image 40
Andy Res Avatar answered Oct 16 '22 08:10

Andy Res