18

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.

Roberto Leinardi
  • 10,641
  • 6
  • 65
  • 69
  • 3
    You'll need to expose `modules`, not `components` from the library, I guess. – EpicPandaForce Nov 08 '16 at 15:14
  • @EpicPandaForce Sorry but I have little experience with Dagger: instead of having Components I've created Modules where I'm providing the instance of my Managers; again it works fine if I don't put any scope but if I try to use the @Singleton scope, the same that I put in the MainComponent, I'm getting the compilation error `Error:Execution failed for task ':app:compileDebugJavaWithJavac'. > java.lang.NoClassDefFoundError: dagger/internal/ScopedProvider` – Roberto Leinardi Nov 08 '16 at 15:29
  • 1
    Some news: the Model suggestion worked, I was having a different version of Dagger in the App module and that was causing the NoClassDefFoundError. I'll write an answer with the working code. – Roberto Leinardi Nov 09 '16 at 13:10

2 Answers2

13

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.

Roberto Leinardi
  • 10,641
  • 6
  • 65
  • 69
  • Can't use custom scope like @AppScope with this approach, if we implement independent library modules. – Sepehr Feb 08 '20 at 15:25
1

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();
Andy Res
  • 15,963
  • 5
  • 60
  • 96