Problems with Hilt DI - android

Sorry for seally question, I have never used Dagger/Hilt before. Can't understand how to inject dependencies in my app. Here is what have now:
#InstallIn(Application::class)
#Module
abstract class RepositoryModule {
#Binds
abstract fun bindProductRepository(productRepository: ProductRepository): IProductRepository
#Binds
abstract fun bindCategoryStorage(categoryStorage: CategoryStorageImpl) : CategoryStorage
companion object {
#Provides
#Singleton
fun createRoomDataBase(#ApplicationContext context: Context) : ProductRoomDatabase = ProductRoomDatabase.getDatabase(context)
#Provides
#Singleton
fun createProductDao(productRoomDatabase: ProductRoomDatabase) = productRoomDatabase.productDao()
#Provides
#Singleton
fun createCategoryDao(productRoomDatabase: ProductRoomDatabase) = productRoomDatabase.categoryDao()
}
}

Hilt comes with three pre-made components:
ApplicationComponent | SingletonComponent - component that contains application-level dependencies and live until application lives.
ActivityComponent - component that contains Activity-level dependencies
FragmentComponent - component that contains Fragment level dependencies
InstallIn annotation suggests that all dependendencies in the current module will be available in the argument of the annotation.

There are many courses and tutorials about this topic, but most of them just rephrase the original documentation. I would strongly suggest you read this documentation, from official Android website
https://developer.android.com/training/dependency-injection/hilt-android

Related

Hilt Android - Why there is a need of #Bind for interface dependency injection when we can perform same thing with #Provide

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?

Dagger hilt: Difference between annotating a class #Singleton and a provides function #Singleton

My question is pretty simple and straightforward: What is the difference between the two annotations / examples:
Example one
#Singleton
class MySingletonClass() {}
#Module
#InstallIn(FragmentComponent::class)
abstract class MyFragmentModule {
#Provides
fun provideMySingletonClass() = MySingletonClass()
}
Eaxmple two
class MySingletonClass() {}
#Module
#InstallIn(FragmentComponent::class)
abstract class MyFragmentModule {
#Singleton
#Provides
fun provideMySingletonClass() = MySingletonClass()
}
The only difference I know is, that the second example gives me the following error:
error: [Dagger/IncompatiblyScopedBindings] FragmentC scoped with #dagger.hilt.android.scopes.FragmentScoped may not reference bindings with different scopes:
Does that mean, that the #Singleton annotation in example one is simply ignored?
In Example One, your #Singleton annotation is ignored, but only because you are calling the constructor yourself in your #Provides method. Because Dagger doesn't interact with your MySingletonClass constructor, it cannot read or use the annotation.
If your #Singleton class MySingletonClass had an #Inject constructor—even an empty one—then Dagger would be able to interact with it directly as long as you also delete the #Provides fun that would override the constructor detection. Once you've done that, the behavior of #Singleton would be the same in either syntax.
Regarding the error message "error: [Dagger/IncompatiblyScopedBindings] XXX scoped with #YYY may not reference bindings with different scopes": #Andrew The real problem here is that in Example Two you're trying to declare a #Singleton binding in a Module that you install in your FragmentComponent. #Singleton bindings can only happen in a #Singleton component, which in Hilt is SingletonComponent. I don't remember for sure, but I think your Example One (with the edits I described) would work with singleton behavior and without an error, because Dagger would automatically select the appropriate component in the hierarchy to install your MySingletonClass.

Dagger Hilt with multiple implementations of an interface

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

Dagger2 + ViewModel + Repository

I am new to Dagger 2 and trying to implement it in Kotlin. Here i am trying to inject my repository object into viewmodel. I am successfully able to inject it this way
public class LoginViewModel #Inject constructor(var mApplication: Application, var repository: LoginRepository) :
ViewModel() {
This is how my repository looks like
class LoginRepository #Inject constructor(val retrofit: APICallInterface) {
This is how my module looks like
#Module
class BaseModule {
#Provides
fun getRetrofit(): APICallInterface {
return Retrofit.Builder()
.baseUrl("https://samples.openweathermap.org/data/2.5/")
.addConverterFactory(GsonConverterFactory.create())
.build().create(APICallInterface::class.java)
}
What i am unable to understand is how Dagger 2 is able to provide an object for repository as i have not mentioned it in any module with #Provides annotation.
I have tried following many blogs and few stckoverflow questions available here but none of them is solving my doubt.
Any help/explanation will be appreciated.
What i am unable to understand is how Dagger 2 is able to provide an object for repository as i have not mentioned it in any module with #Provides annotation.
You're using constructor injection by annotating the constructor with #Inject:
[#Inject] Identifies injectable constructors, methods, and fields.
So, by adding the annotation, Dagger becomes aware of the constructor and knows how to create the object when needed.
class LoginRepository #Inject constructor(..)
If your constructor wouldn't have the annotation on it then you'd need a #Provides annotated method in a module so that Dagger can access the dependency, but you should use #Provides annotated methods primarily for objects that need additional setup and/or initialization.

Using dependencies from multiple modules dagger 2.11 android

Hi I have two modules seperated in domain packages in my project and have firebase insance service class that injects itself manually using DaggerAppComponent.
Then I have two inject dependencies located on the two modules I mentioned.
ModuleOne has the dependency called storage and ModuleTwo has one called delegator.
When atempting to inject both of these inside my firebase service class, it complains that it can't locate and find the delegator injector from ModuleTwo.
However, if I copy the provides method interface of the delegator into ModuleOne it sort of works(now complains that its bound multiple times but can easily fix that by adding naming convention).
This is a hacky way of doing it which I am not keen to do and just want the ability to use any dependencies from different modules. Is this possible?
Below is my firebase service class
class MyInstanceIdService : FirebaseInstanceIdService() {
#Inject
lateinit var delegator: AccountDelegatorContract
#Inject
lateinit var Storage: StorageContract
override fun onCreate() {
super.onCreate()
AndroidInjection.inject(this)
}
override fun onTokenRefresh() {
DaggerApplicationComponent.builder().application(application as MyApplication).build().inject(this)
val refreshToken = FirebaseInstanceId.getInstance().token
// val pianoStorage: PianoStorage = PianoStorage(this)
sendTokenToServer(refreshToken, storage.getUniqueId())
}
private fun sendTokenToServer(refreshToken: String?, uniqueId: String) {
//TODO: Send this new token to server
delegator.sendPushToken(PushTokenRequest(refreshToken!!, uniqueId))
}
Here is moduleOne which represents the main Module that houses dependencies that are used in multiple domain packages in my application.
#Module
abstract class MainModule {
#ContributesAndroidInjector(modules = [AccountModule::class])
#ViewScope
abstract fun bindInstanceIdService(): MyInstanceIdService
#Provides
#Singleton
#JvmStatic
fun provideApplicationContext(application: Application): Context = application
#Provides
#Singleton
#JvmStatic
fun provideStorageData(storage: MainStorage): StorageContract = storage
}
Here is ModuleTwo that is specific to a domain packages
#Module
abstract class AccountModule {
#Module
companion object {
#ViewScope
#JvmStatic
#Provides
fun providesAccountDelegator(accountDelegate: AccountDelegator): AccountDelegatorContract = accountDelegate
#ViewScope
#JvmStatic
#Provides
fun providesAccountControllerContract(accountNetworkController: AccountNetworkController): AccountControllerContract = accountNetworkController
}
}
My app is organised in different packages that represent a part/domain of the app such as accounts, users, vehicle, message etc etc and with each domain has its own module which defines specific dependencies related to that domain.
My issue is that how can I use dependencies above located on different modules?
Edit: my appCOmponent looks like this
#Singleton
#Component(modules = arrayOf(MainModule::class,
AccountModule ::class))
interface ApplicationComponent : AndroidInjector<DaggerApplication> {
fun inject(MyApplication: MyApplication)
fun inject(myInsanceIdService: MyInstanceIdService)
override fun inject(instance: DaggerApplication)
#Component.Builder
interface Builder {
#BindsInstance
fun application(applicaton: Application): Builder
fun build(): ApplicationComponent
}
}
You add you AccountModule (with provision methods of ViewScope sope) to your AppComponent as well as your subcomponent (declared with #ContributesAndroidInjector bindInstanceIdService), so I wonder how/why this compiles at all. So first of all you should remove the AccountModule from your app component, since it has a different scope anyways and won't do you any good there.
Secondly, you call DaggerApplicationComponent.inject(this) in your MyInstanceIdService, although ApplicationComponent does not include a inject(service : MyInstanceIdService) method. Again, I don't see how/why this would compile. As already mentioned above, you registered a subcomponent (#ContributesAndroidInjector bindInstanceIdService) to inject your service, so you should use it. Your app component does not even have the correct scope to provide all of the objects.
Even worse, you create another AppComponent in your MyInstanceIdService, so even if this would work, you now have 2 singleton components providing 2 different instances of your "singleton" objects. Don't recreate components out of their scope. Use MyApplication.getAppComponent() and use that one!
So after removing the AccountModule from the AppComponent you should make your Application implement the HasServiceInjector, injecting/returning the dispatching injector like you do for your activities. Then inject your service by using AndroidInjection.inject(this).
To sum up:
Check your scopes. A #Singleton component can't provide #ViewScope things.
Don't register modules with different scopes on components, they can't use it anyways
Don't inject an object with a component of a different scope than you need
Don't recreate components when creating subcomponents. Use the existing one!
If you use dagger.android then don't create the components yourself, but implement the correct interface and use AndroidInjection.inject()

Categories

Resources