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

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.

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

Dagger2 Found Dependency Cycle 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.

Convert Static method in Dagger Module class to Kotlin

I have following project in Github : https://github.com/Ali-Rezaei/TMDb-Paging which I use Dagger2 for dependency injection.
One of my Module classes is as Follow in java :
#Module
public abstract class DetailModule {
#FragmentScoped
#ContributesAndroidInjector
abstract DetailFragment detailFragment();
#Provides
#ActivityScoped
static Movie provideMovie(DetailActivity activity) {
return activity.getIntent().getExtras().getParcelable(EXTRA_MOVIE);
}
}
As you can see provideMovie method is static. When I convert it to Kotlin :
#Module
abstract class DetailModule {
#FragmentScoped
#ContributesAndroidInjector
internal abstract fun detailFragment(): DetailFragment
companion object {
#Provides
#ActivityScoped
internal fun provideMovie(activity: DetailActivity): Movie {
return activity.intent.extras.getParcelable(EXTRA_MOVIE)
}
}
}
But when I build the project I get following Kotlin compiler error :
error: #Provides methods can only be present within a #Module or #ProducerModule
public final com.sample.android.tmdb.vo.Movie provideMovie$app_debug(#org.jetbrains.annotations.NotNull()
Could be any solution to have the class in Kotlin?
Comanion Object is technically different class and not annotated with #Module. (So you are getting that error)
you need to use JVM Annotations for methods. So Kotlin will create a static method inside DetailModule itself.
Try #JvmStatic
#Module
companion object {
#JvmStatic
#Provides
#ActivityScoped
internal fun provideMovie(activity: DetailActivity): Movie {
return activity.intent.extras.getParcelable(EXTRA_MOVIE)
}
}

Dagger 2 + MVP - single presenter assigned to multiple fragments

I would like to implement a part of an application that takes some steps that would be handled by one presenter. I have declared one scope:
#Scope
annotation class FormScope
next, I wanted to declare a module that would provide necessary dependencies:
#Module
object FormModule {
#JvmStatic
#Provides
fun providesFragmentManager(activity: FragmentActivity): FragmentManager = activity.supportFragmentManager
#JvmStatic
#Provides
fun providesNavigation(fragmentManager: FragmentManager): SobergridCoachingNavigationUnauthorizedFirstStep = SobergridCoachingNavigationUnauthorizedFirstStep(fragmentManager)
#JvmStatic
#Provides
fun providesCoordinator(navigation: NavigationUnauthorized): CoordinatoUnauthorized = CoordinatoUnauthorized(navigation)
#JvmStatic
#Provides
#Reusable
fun providesPresenter(coordinator: CoordinatoUnauthorized): OnboardingFragmentContract.Presenter = FormPresenter(coordinator)
}
and finally, I bind the modules into fragments that I want inject dependencies into:
#Module(includes = [AndroidSupportInjectionModule::class])
abstract class FragmentBindingModule {
#FormScope
#ContributesAndroidInjector(modules = [FormFirstModule::class, FormModule::class])
abstract fun contributesFormFirst(): FormFirstFragment
#ContributesAndroidInjector(modules = [FormSecondModule::class, FormModule::class])
abstract fun contributesFormSecond(): FormSecondFragment
#ContributesAndroidInjector(modules = [FormThirdModule::class, FormModule::class])
abstract fun contributesFormThird(): FormThirdFragment
}
The problem that I encounter is that every single time a new fragment is showed the Dagger creates a new instance of the Presenter. I want to use a single presenter for all of those Fragments. What I do wrong? What should I improve to be able to achieve my goal?
UPDATE
I have also tried annotating my provide method with #Singleton
#JvmStatic
#Provides
#Signleton
fun providesPresenter(coordinator: CoordinatoUnauthorized): OnboardingFragmentContract.Presenter = FormPresenter(coordinator)
but this leads to the compilation error. The last thing that I tried was to put annotations (both #Reusable and #Singleton) before the declaration of the Presenter class. This approach gives me no compilation errors but still, there is more than one instance of the Presenter class.
Move your Presenter provides to FragmentActivity Module with Scope to get the same Presenter for all fragments in Activity
#Module
public class FragmentActivityModule {
//common provides for all fragments
#Provides
#FormScope
public YourPresenter providesYourPresenter() {
return new YourPresenter();
}
....
And your builder have to look like this
#Module
public abstract class ActivityBuilder {
#FormScope
#ContributesAndroidInjector(modules = {FragmentActivityModule.class, Form1FragmentProvider.class
, Form2FragmentProvider.class})
abstract FragmentActivity bindFragmentActivity();
In Form1FragmentModule are provides only for Form1Fragment.
Create FragmentProviders for all fragments Form1FragmentProvider, Form2FragmentProvider...
#Module
public abstract class Form1FragmentProvider {
#ContributesAndroidInjector(modules = Form1FragmentModule.class)
abstract Form1Fragment provideForm1FragmentFactory();
}
Do not forget implement HasSupportFragmentInjector in your FragmentActivity
public class FragmentActivity extends AppCompatActivity implements HasSupportFragmentInjector {
#Inject
DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;
....
#Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return fragmentDispatchingAndroidInjector;
}
Do not forget attach AndroidSupportInjection in your Fragments
#Override
public void onAttach(Context context) {
AndroidSupportInjection.inject(this);
super.onAttach(context);
}

Dependency injection in non-global activity scope done right. How to provide special MyActivity as Activity

I am using dependency injection to provide global objects (#Singleton) and non global objects for activities only (#ActivityScoped).
Now I wonder if I did it right and if it could be done better. The most interesting part of this DI implementation is the injection of the object SomeManager into 2 different activities with restricted scope
Here is the code
The main app component
#Singleton
#Component(modules = [
ApplicationModule::class,
AndroidSupportInjectionModule::class,
ActivityModule::class,
ManagerModule::class,
...
ClientModule::class])
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): AppComponent.Builder
fun build(): AppComponent
}
}
The appclication module
#Module
abstract class ApplicationModule {
#Binds
#Singleton
internal abstract fun bindContext(application: Application): Context
}
The module for the activities
#Module
abstract class ActivityModule {
#ActivityScoped
#ContributesAndroidInjector(modules = [MainActivityModule::class])
internal abstract fun mainActivity(): MainActivity
#ActivityScoped
#ContributesAndroidInjector(modules = [LoginActivityModule::class])
internal abstract fun loginActivity(): LoginActivity
}
And now I want to inject a new SomeManager to the LoginActivity and a new one to the MainActivity.
The approach is having a module for each activity like you see above in the #ContributesAndroidInjector(modules... annotation. The implementations of the 2 files look like this
#Module
object MainActivityModule {
#JvmStatic
#Provides
#ActivityScoped
internal fun provideSomeManager(activity: MainActivity, apiClient: ApiClient) =
SomeManager(activity, apiClient)
}
And
#Module
object LoginActivityModule {
#JvmStatic
#Provides
#ActivityScoped
internal fun provideSomeManager(activity: LoginActivity, apiClient: ApiClient) =
SomeManager(activity, apiClient)
}
The Question:
1) Now the LoginActivityModule and MainActivityModule look very similar. Is there a better approach to provide SomeManager to both activities without making them #Singleton and withouth creating a module for each activity (becaues SomeManager only needs an Activity, not a special sublcass)? I had something in mind like that it only takes an Activity instead of a specific XXXActivity. But how can I tell dagger to provide the XXXActivity as Activity
2) And beside that optimizing in 1), is this a correct implementation?
Update 1
I have solved it by the following implementation. Is this the right way to do this?
Module that provides the MainActivity as Activity
#Module
object MainActivityModule {
#JvmStatic
#Provides
#ActivityScoped
internal fun provideAsActivity(activity: MainActivity): Activity = activity
}
Module that provides the MainActivity as Activity
#Module
object LoginActivityModule {
#JvmStatic
#Provides
#ActivityScoped
internal fun provideAsActivity(activity: LoginActivity): Activity = activity
}
A Manager module that is only #ActivityScoped
#Module
object ManagerModule2 {
#JvmStatic
#Provides
#ActivityScoped
internal fun provideSomeManager(activity: Activity, apiClient: ApiClient) =
SomeManager(activity, apiClient)
}
And the Android injector for the activities
#Module
abstract class ActivityModule {
#ActivityScoped
#ContributesAndroidInjector(modules = [ManagerModule2::class, MainActivityModule::class])
internal abstract fun mainActivity(): MainActivity
#ActivityScoped
#ContributesAndroidInjector(modules = [ManagerModule2::class, LoginActivityModule::class])
internal abstract fun loginActivity(): LoginActivity
}
Now the LoginActivityModule and MainActivityModule look very similar. Is there a better approach to provide SomeManager to both activities without making them #Singleton? I had something in mind like that it only takes an Activity instead of a specific XXXActivity.
Yes, you could do that. You can replace the dependency on the specific Activity and replace it with Activity or Context (depending on your actual needs) and move that declaration into a separate module, which you could include in both of your ActivityModules.
#Module
object SomeManagerModule {
#JvmStatic
#Provides
#ActivityScoped
internal fun provideSomeManager(activity: Activity, apiClient: ApiClient) =
SomeManager(activity, apiClient)
}
And either include it with the module or add it to ContributesAndroidInjector.
#Module(includes = [SomeManagerModule::class])
object MainActivityModule { /* ... */ }
// or
#ActivityScoped
#ContributesAndroidInjector(modules = [LoginActivityModule::class, SomeManagerModule::class])
internal abstract fun loginActivity(): LoginActivity
And you could even remove the need for a module completely by using Constructor Injection.
#ActivityScoped
SomeManager #Inject constructor(activity: Activity, apiClient: ApiClient)
Either way you would have to bind / provide your xxxActivitys as a Activity somewhere so that Dagger can find them.
And beside that optimizing in 1), is this a correct implementation?
Looks good to me. You said you wanted a new manager per Activity, so #ActivityScoped seems the correct choice. You could possibly remove the scope completely if you don't have to ensure that there is only ever one per Activity-scoped component, but this depends on your exact usecase.

Categories

Resources