I'm trying to understand and implement Dagger 2. I've already read a lot of different tutorials and official documentation. I think I understand it in general but I still can not understand some simple points (while I wrote it I've found solution for some but..):
It's possible for #Provides methods to have dependencies of their own.
When it's possible?
What I see it's possible to get "component contains a dependency cycle".
Can someone help me to understand cases when it possible and when it not possible.
Thanks.
Actually, you can use qualifiers (#Named("something") annotation) to get multiple different type of implementation for a given dependency.
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(BaseActivity baseActivity);
#Named("first")
BaseNavigator firstNavigator();
#Named("second")
BaseNavigator secondNavigator();
Context context();
//...
}
#Module
public class ApplicationModule {
private final AndroidApplication application;
public ApplicationModule(AndroidApplication application) {
this.application = application;
}
#Provides
#Singleton
#Named("first")
BaseNavigator provideFirstNavigator() {
return new SomeNavigator();
}
#Provides
#Singleton
#Named("second")
BaseNavigator provideSecondNavigator() {
return new OtherNavigator();
}
#Provides
Context provideApplicationContext() {
return this.application;
}
}
public abstract class BaseActivity extends Activity {
#Inject
#Named("second")
BaseNavigator navigator;
After very long way of many experiments I've found the answer.
I'm making this answer as cue card for myself and hope it can help other daggers jedis.
So we have dagger structure
AndroidApplication
BaseActivity
Navigator
ApplicationComponent
ApplicationModule
...
ApplicationComponent.class
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(BaseActivity baseActivity);
Navigator navigator();
Context context();
//...
}
ApplicationModule.class
#Module
public class ApplicationModule {
private final AndroidApplication application;
public ApplicationModule(AndroidApplication application) {
this.application = application;
}
#Provides
#Singleton
Navigator provideNavigator() {
return new Navigator();
}
#Provides
#Singleton
Context provideApplicationContext() {
return this.application;
}
}
Navigator.class
#Singleton
public class Navigator implements BaseNavigator {
public Navigator() {}
}
BaseActivity.class
public abstract class BaseActivity extends Activity {
#Inject
Navigator navigator;
//code here
}
This code will work and BaseActivity will get navigator as new Navigator() provided by ApplicationModule.
But if you have several implementation of BaseNavigator class you can get some certain implementation for example Navigator class without creating new instance manually.
*This construction will give you "component contains a dependency cycle"
#Provides
#Singleton
Navigator provideNavigator(Navigator navigator) {
return navigator;
}
You can do this:
ApplicationComponent.class
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(BaseActivity baseActivity);
BaseNavigator navigator(); // changed to interface type
Context context();
//...
}
ApplicationModule.class
#Module
public class ApplicationModule {
private final AndroidApplication application;
public ApplicationModule(AndroidApplication application) {
this.application = application;
}
#Provides
#Singleton
BaseNavigator provideNavigator(Navigator navigator) {
return navigator;
} // this will return interface type but with implementation you needed
#Provides
#Singleton
Context provideApplicationContext() {
return this.application;
}
}
Navigator.class
#Singleton
public class Navigator implements BaseNavigator {
#Inject // don't forget to add this annotation to the constructor
public Navigator() {}
}
BaseActivity.class
public abstract class BaseActivity extends Activity {
#Inject
BaseNavigator navigator;// changed to interface type
//code here
}
Now you did not create new instance for Navigator, Dagger did it instead of you in its generated factory.
Related
I have a module as follows.
#Module
public class AppModule {
private final Application app;
public AppModule(Application app) {
this.app = app;
}
#Provides
#Architecture.ApplicationContext
Context provideContext() {
return app;
}
#Provides //scope is not necessary for parameters stored within the module
public Context context() {
return provideContext();
}
#Singleton
#Provides
Application provideApp() {
return app;
}
#Singleton
#Provides
SoundsRepository provideSoundsRepository(Context context, SoundsDAO soundsDAO) {
return new SoundsRepository(context, soundsDAO);
}
}
A component like this.
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
void inject(Global global);
void inject(MainActivity mainActivity);
#Architecture.ApplicationContext
Context getContext();
Application getApplication();
void inject(PostView postView);
void inject(MediaPlayerService mediaPlayerService);
}
In activity, fragment or service, I do this
#Inject
SoundsRepository soundsRepository;
#Override
protected void onCreate(...) {
//....
((Global) getApplication()).getComponent().inject(this);
}
In SoundsRepository
#Singleton
public class SoundsRepository {
#Inject
public SoundsRepository(Context context, SoundsDAO soundsDAO) {
this.context = context;
this.soundsDAO = soundsDAO;
System.out.println(TAG + "INIT");
}
// ....
}
So, now, every time I start to access an activity or service where SoundsRepository is injected, I get a new instance, I mean, the constructor of "SoundsRepository" fires again.
What am I doing wrong?
EDIT : Inject in Application Class
public class Global extends MultiDexApplication {
protected AppComponent appComponent;
private boolean calledAlready = false;
#Override
public void onCreate() {
super.onCreate();
//if (LeakCanary.isInAnalyzerProcess(this)) return;
//LeakCanary.install(this);
initFirebasePersistance();
appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
appComponent.inject(this);
FrescoUtil.init(getApplicationContext());
}
public AppComponent getComponent() {
return appComponent;
}
}
In your module you have a method that provides an instance of SoundsRepository - good
In your AppComponent you are missing a:
SoundsRepository soundsRepository();
In your Global which extends Application/MultidexApplication you create your DaggerAppComponent - good
In your other activities/fragments/services just call:
Global application = (Global) getApplication();
SoundsRepository sr = application.getComponent().soundsRepository()
Android guarantees you have only one instance of your Application (Global) class for all other actvities/services (its somewhat like a singleton).
So keep your component in that application class, and whenever you need your class, call: (YourApplication) getApplication().getComponent().yourSingleInstanceSomething();
I created and tested sample code for you: https://github.com/zakrzak/StackDaggerTest
Dagger's #Singleton is just a scope, and does not guarantee returning a singular instance of a class.
In my understanding, if you:
void inject(PostView postView);
you tell Dagger to make everything you annotated with #Provided in AppModule accessible in your PostView as soon as you request it with:
#Inject
SoundsRepository soundsRepository;
then dagger just calls the #provided method which in your case returns a new SoundRepository instance:
#Singleton
#Provides
SoundsRepository provideSoundsRepository(Context ........) {
return new SoundsRepository(...);
}
which causes your problem
I have a simple situation here which I am not able to get around (since today is my second day with dagger).
I have a RepositoryManager class the intent of which is to house all repositories like(StudentRepo , TeacherRepo etc etc)
This is my RepositoryManager
public class RepositoryManager {
private static RepositoryManager INSTANCE = null;
#Inject
StudentRepo studentRepo;
#Inject
TeacherRepo teacherRepo;
private final List<Repository> repositories;
private Context context;
#Inject
public RepositoryManager(Context context) {
this.repositories = new ArrayList<>();
this.context = context;
addRepositories();
}
private void addRepositories() {
addRepository(studentRepo);
addRepository(teacherRepo);
}
I understand why my studentRepo and teacherRepo are null here. It is because I have not asked Dagger to fetch them for me . I believe I am missing some very important aspect of Dagger here in which we can explicitly fetch instances of our desired objects.
The StudentRepo btw has its own module and I can easily pull it out in an activity.
My question is just how to fetch instances in a non activity class.
my AppComponent
#Singleton
#Component(modules =
{
AndroidInjectionModule.class,
ApplicationModule.class,
ActivityBindingModule.class,
RepositoryModule.class})
public interface AppComponent extends AndroidInjector<ChallengerHuntApplication> {
RepositoryManager exposeRepositoryManager();
#Component.Builder
interface Builder {
#BindsInstance
AppComponent.Builder application(Application application);
AppComponent build();
}
}
RepositoryModule
#Module(includes = SystemRepositoryModule.class)
public class RepositoryModule {
}
Student Repository Module
#Module
public class StudentRepositoryModule {
#Singleton
#Provides
#Local
public StudentDataSource provideStudentLocalDataSource(Context context) {
return new StudentLocalDataSource(context);
}
#Singleton
#Provides
#Remote
public StudentDataSource provideStudentRemoteDataSource(Context context) {
return new StudentRemoteDataSource();
}
}
I'm having problems with the dagger dependency cycle, and despite looking for hours, I can not find the solution. I think it's my idea of architecture. What could be wrong? I'm using Dagger 2.11.
Following the codes
Inicio.java
public class Inicio extends BaseFragment implements InicioMvpView {
#Inject
InicioMvpPresenter inicioPresenter;
#Inject
MainMvpPresenter<MainMvpView> mainPresenter;
//...
InicioComponent.java
#Subcomponent(modules = {InicioModule.class})
public interface InicioComponent extends AndroidInjector<Inicio> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<Inicio> {
}
}
InicioModel.java
public class InicioModel implements InicioMvpModel{
#Inject
InicioMvpPresenter inicioPresenter;
#Inject
public InicioModel() {
}
#Override
public void recuperarAgendamentos() {
//...
inicioPresenter.atualizarListaAgendamentos(agendamentos);
}
}
InicioModule.java
#Module
public class InicioModule {
#Provides
InicioMvpView provideInicioView(Inicio inicioFragment){
return inicioFragment;
}
#Provides
InicioMvpPresenter provideInicioPresenter(
InicioPresenter presenter) {
return presenter;
}
#Provides
InicioMvpModel provideInicioModel(InicioModel inicioModel) {
return inicioModel;
}
}
InicioPresenter.java
public class InicioPresenter implements InicioMvpPresenter{
#Inject
InicioMvpModel inicioModel;
#Inject
InicioMvpView inicioMvpView;
#Inject
public InicioPresenter() {
}
#Override
public void recuperarAgendamentos(Bundle savedInstanceState) {
//..
}
}
Your problem is that you trying to solve a ciclic dependency with Dagger and Dagger doesn't solve this problem.
This can be corrected in your architecture. Just pass a callback to your model instead of passing the Presenter to the Model.
This this:
public class InicioModel implements InicioMvpModel{
#Inject
public InicioModel() {
}
#Override
public void recuperarAgendamentos(Presenter inicioPresenter) {
//...
inicioPresenter.atualizarListaAgendamentos(agendamentos);
}
}
That's it. Just pass the presenter as a parameter in your methods in your model. This makes the communication less coupled.
You can also take a look for RxJava, it removes the need to pass the presenter in the method. Link: https://github.com/ReactiveX/RxJava
There is also a good implementation to follow for MVP by Antonio Leiva:
https://github.com/antoniolg/androidmvp
The dependencies provided via the application component of the app do not have a single instance.
I have the following code:
#Component (modules = AppModule.class)
public interface AppComponent {
SharedPreferences getSharedPreferences();
}
#Module
public class AppModule {
private Context appContext;
private String prefFile;
public AppModule(#NonNull Context appContext, #NonNull String prefFile) {
this.appContext = appContext;
this.prefFile = prefFile;
}
#Provides
public SharedPreferences providePreferences(){
return new AppSharedPreferences(appContext, prefFile);
}
}
#Singleton
public class AppSharedPreferences implements SharedPreferences{
public AppSharedPreferences(Context appContext, String prefFile)
//Some code
}
public class AppApplication extends Application {
private AppComponent appComponent;
private InteractorsComponent interactorsComponent;
#Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder().appModule(new AppModule(getApplicationContext(), "PREF_STORE_NAME")).build();
interactorsComponent = DaggerInteractorsComponent.builder().appComponent(appComponent).build();
}
public AppComponent getAppComponent() {
return appComponent;
}
public InteractorsComponent getInteractorsComponent() {
return interactorsComponent;
}
}
#Module
abstract class InteractorsModule {
#Singleton
#Binds
abstract InteractorA provideInteractorA(AppInteractorA interactor);
#Singleton
#Binds
abstract InteractorB provideInteractorB(AppInteractorB interactor);
}
#Singleton
#Component (modules = InteractorsModule.class, dependencies = AppComponent.class)
public interface InteractorsComponent {
InteractorA getInteractorA();
InteractorB getInteractorB();
}
#Singleton
class AppInteractorA implements InteractorA {
private AppSharedPreferences pref;
#Inject
public AppInteractor(#NonNull AppSharedPreferences pref) {
this.pref = pref;
}
//Other overridden methods
}
#Singleton
class AppInteractorB implements InteractorB {
private AppSharedPreferences pref;
#Inject
public AppInteractor(#NonNull AppSharedPreferences pref) {
this.pref = pref;
}
//Other overridden methods
}
Now, InteractorA and InteractorB to be injected in someother classes.
InteractorA and InteractorB instances themselves are singleton where ever they are injected. But of object of InteractorA and object of InteractorB, a different instance of SharedPreferences class is provided. So InteractorA has a different instance of SharedPreferences and InteractorB has a different instance of SharedPreferences.
Could someone help to make sure that both InteractorA and InteractorB have the same instance of SharedPreferences.
Thanks.
#Module
public class AppModule {
// ...
#Provides
public SharedPreferences providePreferences(){
return new AppSharedPreferences(appContext, prefFile);
}
}
Though AppSharedPreferences is annotated with #Singleton, you're calling its constructor yourself, which means that Dagger can't guarantee that there's only one SharedPreferences instance in your application. This means that you'll get a new AppSharedPreferences instance whenver you inject SharedPreferences, including through the InteractorsComponent component dependency.
You should mark providePreferences with #Singleton, or use an #Inject constructor on AppSharedPreferences so that Dagger can manage its #Singleton behavior.
I create a proxy class using a APT(Annotation Processor) to do some instrumentation in the original class.
I want to change a binding in Dagger 2 with this proxy instead of the default implementation when the implementation class is annotated with #MyCustomAnnotation.
Implementation(By user):
#MyCustomAnnotation
public class MyServiceImpl implements MyService {
// Implementation
}
Original Module:
#Module
public class AppModule {
#Provides
#Singleton
MyService providesService() {
return new MyServiceImpl();
}
}
Final module replacing the implementation:
#Module
public class AppModule {
#Provides
#Singleton
MyService providesService() {
return new MyServiceImplProxy();
}
}
I need something transparent from a user perspective.
What do you guys suggest?
Well, I ended up using #Binds and doing a double bind.
Changed the user module to a simple #Binds:
#Module(includes = { OverrideModule.class })
public abstract class AppModule {
#Binds
#Singleton
abstract MyService providesMyService(MyServiceImpl impl);
}
And created a override module:
#Module
public abstract class OverrideModule {
#Binds
abstract MyServiceImpl providesMyServiceImpl(MyServiceImplProxy impl);
}