With Dagger2 it's easy to explicitly create components and list their dependencies. But I can't seem to find a way to provide different implementations of an interface to lets say a fragment.
For example, my app has 2 production modes: paid and free.
I have a PaidActivity and a FreeActivity, both of which start exactly the same Dashboard fragment with an Analytics class. For Paid I provide a PaidAnalytics implementation, for Free I provide a FreeAnalytics implementation.
With Dagger2 it's easily achieved by just listing a Paid or a Free Module in the Activity's Component (or Subcomponent).
#Module
abstract class FreeActivityModule {
#ContributesAndroidInjector(
modules = [
FreeAnalyticsModule::class,
DashboardFragmentModule::class
]
)
abstract fun injectFreeActivity(): FreeActivity
}
#Module
abstract class PaidActivityModule {
#ContributesAndroidInjector(
modules = [
PaidAnalyticsModule::class,
DashboardFragmentModule::class
]
)
abstract fun injectPaidActivity(): PaidActivity
}
#Module
abstract class DashboardFragmentModule {
#ContributesAndroidInjector
abstract fun injectDashboardFragment(): DashboardFragment
}
class DashboardFragment : Fragment() {
...
#Inject
lateinit var analytics: Analytics
...
}
With Dagger Hilt I couldn't find a way to do this.
With Dagger it is impossible to replace dependencies at runtime in my use case.
During one of the Google Hilt sessions they recommended to use an if else statement in a Provides method: https://youtu.be/i27aNF-kYR4?t=3355 (that's what I prefer to avoid).
The answer above doesn't understand my question, because they are qualifying dependencies at compile time which I can't do. Since my fragment never knows the place it's used and I don't want to just duplicate code.
Here's an example where you can see exactly what I'm doing and that it can't be achieved with Hilt by design: https://github.com/dmytroKarataiev/daggerAndroidvsHiltRuntimeDependencies
I think we could leverage Hilt's Qualifier feature to solve this multi binding issue.
Here is some resources I found: https://developer.android.com/codelabs/android-hilt#8
I quote:
To tell Hilt how to provide different implementations (multiple bindings) of the same type, you can use qualifiers.
I think it's a way for Hilt to differentiate different implementations of the same interface.
To setup your Hilt module:
#Qualifier
annotation class PaidModule
#Qualifier
annotation class FreeModule
#InstallIn(ActivityComponent::class)
#Module
abstract class PaidActivityModule {
#ActivityScoped
#Binds
#PaidModule
abstract fun bindPaidModule(impl: PaidActivity): YourInterface
}
#InstallIn(ActivityComponent::class)
#Module
abstract class FreeActivityModule {
#ActivityScoped
#Binds
#FreeModule
abstract fun bindFreeModule(impl: FreeActivity): YourInterface
}
To inject:
#FreeModule
#Inject
lateinit var freeActivity: YourInterface
#PaidModule
#Inject
lateinit var paidActivity: YourInterface
Related
I need a solution where i could inject Fragment arguments/extras (one Longand one String to be specific) into Fragment's viewmodel and based on these values viewmodel could run it's setup in init {..}
All the ViewModel's injections are fine, dependencies in ViewModel's #Inject construct([dependencies]){..} are provided and working correctly.
My SubComponent looks like this at the moment :
`
#Scope
annotation class MyFragmentScope
#MyFragmentScope
#Subcomponent(modules = [MyFragmentModule::class])
interface MyFragmentSubcomponent : Injector<MyFragment> {
#Subcomponent.Builder
interface Builder : Injector.Factory<MyFragment>
}
#Module
abstract class MyFragmentModule {
#Binds
#IntoMap
#MyFragmentScope
#ViewModelKey(MyFragmentViewModel::class)
abstract fun bindMyFragmentViewModel(
viewModel: MyFragmentViewModel
): ViewModel
}
`
I would be super thankful for any support
Cheers
I tried to create a new ViewModelFactory with additional parameter, but sadly that didn't work out
Tried to use Hilt or Assisted Injection, which also didn't work out since my project is strictly restricted to Dagger v 2.16, if i try to update - tons of bugs arise from this old codebase, it would take months rewriting everything.
Maybe i just did something wrong
I have checked all the documentation and knows well that #Binds used for providing interface dependency.But I am bit confused in current example, lets say I have dependency injection for my interface using #Binds in blow code:
interface MyRepository{
}
#Module
abstract class RepositoryModule{
#Binds
abstract fun bindMyRepository(myRepo:MyRepositoryImpl):MyRepository
}
class RegisterUseCase #Inject constructor(private val myRepo:MyRepository)
But the same thing I can also acheive using #Provides
#Module
class RepositoryModule{
#Provides
fun provideMyRepository():MyRepository{
return MyRespositoryImpl()
}
}
We can acheive same goal by using both binds and provides this thing confusing me. Please explain in details so that I can be more clear with my concept about #Binds and #Provides?
How to inject abstract class
I have an abstract class
abstract class FactoryMediator : xx, yy,zz {}
I am trying to initiate the object for this class using #Module and #Binds annotations. But that is failed to create and throw an error.
#Module
#InstallIn(SingletonComponent::class)
abstract class PolicyModule {
#Binds
abstract fun bindFactoryMediator(factoryMediator: FactoryMediator): PolicyFactoryMediator
}
But this is giving error. What mistake I did? Please can you let me know how to create for abstract class. How can we inject abstract class?
From documentation, it says.
Sometimes a type cannot be constructor-injected. This can happen for multiple reasons. For example, you cannot constructor-inject an interface. You also cannot constructor-inject a type that you do not own, such as a class from an external library. In these cases, you can provide Hilt with binding information by using Hilt modules.
A Hilt module is a class that is annotated with #Module. Like a Dagger module, it informs Hilt how to provide instances of certain types. Unlike Dagger modules, you must annotate Hilt modules with #InstallIn to tell Hilt which Android class each module will be used or installed in.
You have the method syntax slightly wrong, you currently have:
#Binds
abstract fun bind(abstractClass: AbstractClass): ImplementationClass
whereas they should be the other way around, so:
#Binds
abstract fun bind(implementationClass: ImplementationClass): AbstractClass
I have a multi-module project that follows Clean Architecture principles. I have app, presentation, domain, data and remote modules. I am also using Android Architecture Components and apply MVVM in the presentation layer.
A simple ViewModel class looks like this
class SignUpViewModel #Inject internal constructor(
private val signUpInfoMapper: Mapper<SignUpInfo, SignUpInfoEntity>,
private val signUpTask: SignUpTask
) : ViewModel() {
// LiveData and methods
}
Every ViewModel has a dependency on a task(use-case)from the domain module and one or more mapper. Every use-case has a dependency on a repository class from the data module. Every repository class has a dependency on the API from the remote module. So there are chains of dependencies.
I am injecting the ViewModels as below and the ViewModelModule belongs to Singleton AppCpmponent.
#Module
abstract class ViewModelModule {
#Binds
internal abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(SignUpViewModel::class)
internal abstract fun signUpViewModel(viewModel: SignUpViewModel): ViewModel
}
#Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
#kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
#MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
At the moment every ViewModel dependency and sub-dependencies annotated with Singleton scope. It works but annotating all these classes as Singleton might not be a good idea as the project is growing and the number of singleton classes that will live for the entire life cycle of the app is increasing. So I thought that having a different Dagger 2 component for each feature and necessary modules with a different scope would solve the problem but ViewModels can't be annotated with feature scopes.
I wonder what is the best practice to apply dependency injection via Dagger 2 in a multi-module project with MVVM and Clean Architecture.
Any help would be appreciated.
I am not certain on the purpose for Dagger2's #Bind annotation.
From what i have read online im still not clear but here is an example:
#Module
public abstract class HomeModule {
#Binds
public abstract HomePresenter bindHomePresenter(HomePresenterImp
homePresenterImp);
}
and the class definitions look like this:
public interface HomePresenter {
Observable<List<User>> loadUsers();
}
public class HomePresenterImp implements HomePresenter {
public HomePresenterImp(){
}
#Override
public Observable<List<User>> loadUsers(){
//Return user list observable
}
}
why would i need to use #Binds if i can just use provides annotation as follows:
#Provides
public HomePresenter provideHomePresenter() {
return new HomePresenterImp();
}
what is the usecase for #Binds instead of #Provides ? if i use #Binds do i still need to declare it in my appcomponent (its an abstract class when i use #Binds)?
#Binds can be perfectly equivalent to a #Provides-annotated method like this:
#Provides
public HomePresenter provideHomePresenter() {
return new HomePresenterImp();
}
...though you'd probably prefer a variant that takes HomePresenterImp as a method parameter, which lets Dagger instantiate HomePresenterImp (assuming it has an #Inject constructor) including passing any dependencies it needs. You can also make this static, so Dagger doesn't need to instantiate your Module instance to call it.
#Provides
public static HomePresenter provideHomePresenter(HomePresenterImp presenter) {
return presenter;
}
So why would you choose #Binds instead? Dagger has a FAQ about it, but it boils down do these reasons:
#Binds is (slightly) more compact: You can skip the implementation.
#Binds works in interfaces and abstract classes, which are strictly required for Dagger features like #BindsOptionalOf and #ContributesAndroidInjector.
#Binds helps your code stay efficient. #Provides methods can be instance methods, which require Dagger to instantiate your Module in order to call them. Making your #Provides method static will also accomplish this, but your #Provides method will still compile if you forget the static. #Binds methods will not.
#Binds prevents Dagger from having to codegen and keep a separate Factory/Provider for the object, since Java doesn't give Dagger access to know that the implementation is as simple as it is. In your case, Dagger can cast the Provider<HomePresenterImp> to a Provider<HomePresenter> and only keep one, rather than keeping one for HomePresenter that does nothing but call the one for HomePresenterImp.
Thus, the entire thing would be well-represented as:
#Binds abstract HomePresenter bindHomePresenter(HomePresenterImp presenter);
Thanks to this source: https://www.valueof.io/blog/inject-provides-binds-dependencies-dagger-hilt
#Binds:
single param
less code
However, the advantage of using #Binds is that it reduces the amount of code generated (such as Module Factory classes). Less code to generate means the Kapt plugin has less work to do which can speed up build times in larger projects.
#Binds is a very specialized annotation though—it’s used to map an interface to an implementation. It can take only a single parameter and the type return is the interface implemented by the given parameter object.
If the implementation you are binding to takes constructor parameters, you can use a combination of #Inject and #Binds as shown in the example below:
Here a concrete case where you need Bind annotation, imagine you got a BaseActivityModule which is include in all your activity modules that provides your activity viewmodel.
#Module
object BaseActivityModule {
#Provides
#ActivityScope
#ActivityContext
#JvmStatic
fun provideViewModelProvider(
activity: AppCompatActivity,
viewModelFactory: ViewModelProvider.Factory
): ViewModelProvider = ViewModelProviders.of(activity, viewModelFactory)
}
Here you see we need to provide an AppCompatActivity and a ViewModelProvider.Factory. You cannot provide AppCompatActivity with a Provide annotation since activities are created by android.
We're assuming your concrete ActivityModule for example MainActivityModule will provide MainActivity class either because you create a MainActivity sub component or you used ContributesAndroidInjector to automatically create your sub components (but this is another talk).
So we have our MainActivityModule providing MainActivity and our MainActivityModule includes our BaseActivityModule which need an AppCompatActivity. So here the Bind magic, let's tell Dagger that when you need an AppCompatActivity you can use our MainActivity.
#Module(includes = [BaseActivityModule::class])
abstract class MainActivityModule {
#Binds
#ActivityScope
abstract fun bindActivity(activity: MainActivity): AppCompatActivity
}
You can see more from my project template here
Dagger gives a great explanation in their FAQ page. Apparently, Dagger already saw this question coming :)
link summary below:
#Provides serves three functions:
Declare which type (possibly qualified) is being provided -this is the return type
Declare dependencies — these are the method parameters
Provide an implementation for exactly how the instance is provided — this is the method body
#Binds methods are a drop-in replacement for Provides methods that simply return an injected parameter. #Binds methods are abstracts methods without implementations