Dagger2 Found Dependency Cycle Android - android

Im learning and newer to Dagger2 and stuck in the issue where I need to use both #Provides and #Binds in a Module. but its giving error
[Dagger/DependencyCycle] Found a dependency cycle:
public interface CarComponent{
^
com.stupido.daggertutorial.Engine is injected at
com.stupido.daggertutorial.CarModule.bindsEngine(arg0)
com.stupido.daggertutorial.Engine is injected at
com.stupido.daggertutorial.CarModule.providesCar(engine, …)
com.stupido.daggertutorial.Car is requested at
com.stupido.daggertutorial.CarComponent.getCar()
before I was using #Provides it worked fine but with combination I get above issue.
Component
#Component(modules = CarModule.class)
public interface CarComponent{
//constructor injection
Car getCar();
}
Module
#Module
public abstract class CarModule {
#Binds
abstract Wheels bindsWheels(Wheels wheels);
#Binds
abstract Engine bindsEngine(Engine engine);
#Provides
static Car providesCar(Engine engine,Wheels wheels){
return new Car(wheels,engine);
}
}
Activity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
CarComponent carComponent = DaggerCarComponent.create();
Car car = carComponent.getCar();
}
}

#Binds is usually for the case when you have some inherited class and its interface and you want dagger to know this and be able to inject the interface. Usually you'd have
#Binds
abstract Engine bindsEngine(DieselEngine dieselEngine);
and then you can just inject engine without knowing the implementation detail (it's diesel engine).
In your case, if you remove both #Binds methods, it should start working (if there's no other issue).

You are using #Binds in the wrong way. Please read their documentation.
A #Binds method:
Must have a single parameter whose type is assignable to the return type. The return type declares the bound type (just as it would for a #Provides method) and the parameter is the type to which it is bound.
You have to change #Binds to #Provides.

Related

Can we say that Dagger #Bind is more SRP-based than #Provides is?

I'm wondering if I understand SRP correctly using the Dagger under the hood as example.
#Binds processed code
For example, if I have
#Module
abstract class ModuleA {
#Binds
abstract fun provideStorage(storage: SharedStorage): Storage
}
In such a case annotation processor generates the follow code to initialize dagger graph:
private void initialize() {
this.sharedStorageProvider = SharedStorage_Factory.create();
}
#Provides processed code
And if I have (make concrete class and instance (non-static) function):
#Module
class StorageModule {
#Provides
fun provideStorage(): Storage {
return SharedStorage()
}
}
Annotation processor generates the follow code:
private void initialize(final StorageModule storageModuleParam) {
this.provideStorageProvider = StorageModule_ProvideStorageFactory.create(storageModuleParam);
}
Question:
Am I right if I say that #Binds annotation is designed in such a way that it follows the SRP principle more than #Provides?
I've found this post (last dot) about separation, but haven't found thoughts about SRP

Why Dagger2 inject the same object but with 2 different instances?

ArticlesContract.Presenter is a new instance in Adapter, which is different with ArticleListFragment, so my data was lost ! I have no idea why I got two different instances:
#Module
public class ArticleListFragmentModule {
#Provides
ArticlesContract.Presenter provideArticlesPresenter(ArticlesPresenter presenter) {
return presenter;
}
}
public class ArticleListFragment extends DaggerFragment implements ArticlesContract.View {
#Inject
ArticlesContract.Presenter mPresenter; //one instance
}
public class ArticlesAdapter extends RecyclerView.Adapter<ArticleViewHolder> {
#Inject
ArticlesContract.Presenter mPresenter; //another different instance
}
2018-05-16 UPDATED: this issues is fixed by following #Fred answer : lack a scope and managing this scope:
#Scope
#Documented
#Retention(RetentionPolicy.RUNTIME)
public #interface ArticlesScope {
}
#Module
public abstract class ActivityBuilder {
#ContributesAndroidInjector(modules = ArticleListFragmentModule.class)
#ArticleListScope
abstract ArticleListFragment bindArticleListFragment();
}
#Module
public class ArticleListFragmentModule {
/**
* Provide dependency for interface.
* Interface cannot be annotated with #Inject, otherwise it will cause, error: ArticlesContract.Presenter cannot be provided without an #Provides- or #Produces-annotated method.
*/
#Provides
#ArticleListScope
ArticlesContract.Presenter provideArticlesPresenter(ArticlesPresenter presenter) {
return presenter;
}
}
Scoping with #ContributesAndroidInjector, refer to Dagger 2 Annotations: #Binds & #ContributesAndroidInjector
This is because you lack a scope and managing this scope. You need to create a scope yourself or use one provided by dagger already. Here since it's a presenter it can be the Reusable scope I guess or Singleton. However, Singleton has a performance impact that might not be desirable.
The next important thing is that you need to understand that while the component's instance is the same the provided binding will be the same (excluding the case of the Reusable scope). In other words, scopes provide a way of telling dagger - "while this component is alive and it's scope is X then all instances scoped with X will be the same". Here's what I mean in code:
#Scope
#Documented
#Retention(RUNTIME)
public #interface PresenterScope {}
#Module
public class ArticleListFragmentModule {
#Provides
#PresenterScope
ArticlesContract.Presenter provideArticlesPresenter(ArticlesPresenter presenter) {
return presenter;
}
}
I don't know how you've set up your component, but you'd have to annotate it also with the PresenterScope. Now, it's just a matter of making sure that when you inject ArticleListFragment and ArticlesAdapter you'll be using the same instance of the component. If you rebuild the component than the instance of the presenter will be different.
Remember that Reusable is a bit different, but it should suit your needs here since the presenter should hold no state.
Hope this helps

Dagger 2 scopes and multiple bounds

In Dagger 2 Android, I was really liking the new simplified API, but I now require a little bit of customisation and it's making my head hurt.
I have a very common scenario: an app with some activities and every dependency is injected.
I'll describe my current implementation first and the problem afterwards. Bear with me for a second, please.
I started by creating the regular ActivitiesModule
#Module
abstract class ActivitiesModule {
#PerActivity
#ContributesAndroidInjector(modules = MainActivityModule.class)
abstract MainActivity bindMainActivity();
// More activities here, all with the scope #PerActivity
}
and then my MainActivityModule.java (and all the other activity modules) look like this
#Module
public abstract class MainActivityModule {
#Binds
#ActivityContext
#PerActivity
abstract Context provideActivityContext(MainActivity mainActivity);
#Binds
#PerActivity
abstract Activity provideActivity(MainActivity mainActivity);
#Binds
#PerActivity
abstract MainMvpView provideView(MainActivity mainActivity);
}
This is perfect, because every time I needed the activity or context in a class, I could just do this, without tightly coupling my object to a specific activity.
#PerActivity
public class MainPresenter {
private final MainMvpView view;
#Inject
MainPresenter(MainMvpView view) {
this.view = view;
}
}
This is great, because I don't need to declare a #Subcomponent if I don't have to. However, now I want to inject a custom view into my MainActivity subcomponent, and I'm having issues going back to the old dagger.android way of doing things.
So far, I replaced the ContributesAndroidInjector with the old way, like this
#Binds
#IntoMap
#ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindMainActivityInjectorFactory(MainActivitySubcomponent.Builder builder);
and I created a new MainActivitySubcomponent that looks like this
#PerActivity
#Subcomponent
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
#Subcomponent.Builder
abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}
The module pretty much stayed the same. The problem is that I'm getting lots of errors that say that Activity and Context are bound multiple times.
So my 2 questions are:
Is it not possible to have #Binds with Activity and Context if they are scoped so that I can keep things totally decoupled?
Is there an alternative to do view injections using the latest from dagger.android?
Any suggestions will be much appreciated.

Dagger2 : How to use #Provides and #Binds in same module

I'm using the new Dagger2 (ver 2.11) and I'm using the new features like AndroidInjector, and ContributesAndroidInjector. I have an activity subcomponent,
#Module
abstract class ActivityBuilderModule {
#ContributesAndroidInjector(modules =
{UserListModule.class, MainFragmentModule.class})
#ActivityScope
abstract MainActivity bindsMainActivity();
}
#Module
public abstract class MainFragmentModule {
#ContributesAndroidInjector
#FragmentScope
#FragmentKey(UserListFragment.class)
abstract UserListFragment bindsUserListFragment();
}
And the UserListModule provides dependencies for the fragment. Some of the dependencies I just want to bind the instances , and return , like
#Binds
#ActivityScope
abstract UserListView mUserListView(UserListFragment userListFragment);
Instead of simply just return the dependency , like
#Provides
#ActivityScope
UserListView mUserListView(UserListFragment userListFragment){
return userListFragment;
}
My module contains some #Provides methods as well. Can we use both #Binds and #Provides methods in the same module? I tried as shown below
#Module
public abstract class UserListModule {
#Provides
#ActivityScope
UserListFragment mUserListFragment() {
return new UserListFragment();
}
#Binds
#ActivityScope
abstract UserListView mUserListView(UserListFragment userListFragment);
// other provides and binds methods...
......
.....
}
And it its throwing error
Error:(22, 8) error: dagger.internal.codegen.ComponentProcessor was unable to process this interface because not all of its dependencies could be resolved. Check for compilation errors or a circular dependency with generated code.
Is there any way to do this?
#Binds and #ContributesAndroidInjector methods must be abstract, because they don't have method bodies. That means that they must go on an interface or abstract class. #Provides methods may be static, which means they can go on abstract classes and Java-8-compiled interfaces, but non-static ("instance") #Provides methods don't work on abstract classes. This is explicitly listed in the Dagger FAQ, under the sections "Why can’t #Binds and instance #Provides methods go in the same module?" and "What do I do instead?".
If your #Provides method doesn't use instance state, you can mark it static, and it can go onto an abstract class adjacent to your #Binds methods. If not, consider putting the bindings like #Binds and #ContributesAndroidInjector into a separate class--possibly a static nested class--and including that using the includes attribute on Dagger's #Module annotation.
A little addition to Jeff's solution above:
you may create inner interface instead of static inner class, like this:
#Module(includes = AppModule.BindsModule.class)
public class AppModule {
// usual non-static #Provides
#Provides
#Singleton
Checkout provideCheckout(Billing billing, Products products) {
return Checkout.forApplication(billing, products);
}
// interface with #Binds
#Module
public interface BindsModule {
#Binds
ISettings bindSettings(Settings settings);
}
}
In kotlin, you can leverage companion object
#Module
abstract class MyDaggerModule {
#Binds
abstract fun provideSomething(somethingImpl: SomethingImpl): Something
companion object {
#Provides
fun provideAnotherThing(): AnotherThing {
return AnotherThing()
}
}
}
This is other type solution: Add modules to other module after that you can call top module in your component interface. It can be more efficiency because you can use abstract and static.
Details and examples are below:
For example, we have an component interface and two modules such as ComponentClasses, Module_ClassA and Module_ClassB.
Module_ClassA is:
#Module
public class Module_ClassA {
#Provides
static ClassA provideClassA(){
return new ClassA();
}
}
Module_ClassB is:
#Module
abstract class Module_ClassB {
#Binds
abstract ClassB bindClassB(Fragment fragment); //Example parameter
}
So now, we have two models. If you want use them together, you can add one of them to other. For example: You can add Module_ClassB to Module_ClassA:
#Module(includes = Module_ClassB.class)
public class Module_ClassA {
#Provides
static ClassA provideClassA(){
return new ClassA();
}
}
Finally, you do not need to add both modules to your component class. You can only add your top module on your component class, like that:
ComponentClasses is:
#Component(modules = Module_ClassA)
public interface ComponentClasses {
//Example code
ArrayList<CustomModel> getList();
}
However, you should be careful because you need to add your top module. Thus, Module_ClassA added on ComponentClasses interface.

Dagger : Why does dagger require a #inject constructor for an object that does't depend on another object

I think I'm missing something. I get this error:
PostsVM cannot be provided without an #Inject constructor or from an
#Provides-annotated method.
Assume classes as follows :
#Module
public class AppModule {
private final Application mApplication;
#Singleton
#Provides
ViewModel provideListViewModel() {
return new PostsVM();
}
}
And a class PostVM
#Singleton
public class PostsVM extends ViewModel {
public boolean get(){
return true;
}
}
And a component :
#Singleton
#Component(modules = AppModule.class)
public interface AppComponent {
void inject(Global global);
void inject(MainActivity mainActivity);
#Architecture.ApplicationContext
Context getContext();
Application getApplication();
}
And in activity :
#Inject
public ViewModelProvider.Factory factory;
#Override
protected void onCreate(Bundle savedInstanceState) {
InjectorClass.inject(this);
As you can see, the example given for PostVM class, does't depend on anything, why do I need an #inject constructor in it?
tl;dr To prevent errors and to follow convention.
From the JavaDoc of #Inject you can read:
Injectable constructors are annotated with #Inject and accept zero or more dependencies as arguments. #Inject can apply to at most one constructor per class.
And it's always good practice to follow the convention / documentation.
So #Inject marks an entry point for Dagger to signal it how and where to create your class. It is a clear sign of how you intend your class to be used.
What if you have multiple constructors?
What if you require additional setup and should use a #Module instead?
By just defaulting to a no-args constructor (if possible) things could start breaking very easily and you might not be able to pinpoint the source easily if you just assume Dagger does its job.
__ cannot be provided without an #Inject constructor or from an #Provides-annotated method.
This error on the other hand gives you a strong signal that you're missing something and cannot be ignored.

Categories

Resources