I have an app component and a dependent component. The app component declares explicit dependencies, and the dependent component can inject those. However, when I have a dependency that I have to disambiguate with a #Qualifier, the dependent component is not able to inject that dependency.
This is the app component
#Component(modules = [AppModule::class, SchedulersModule::class, StorageModule::class])
#ApplicationScope
interface AppComponent {
fun inject(app: Application)
/* other stuff omitted for brevity */
val bitmapCache: BitmapCache
#UiScheduler fun uiScheduler(): Scheduler
}
This is the scheduler module:
#Module
class SchedulersModule {
#ApplicationScope
#Provides
#IoScheduler
fun provideIoScheduler(): Scheduler = Schedulers.io()
#ApplicationScope
#Provides
#UiScheduler
fun provideMainThreadScheduler(): Scheduler = AndroidSchedulers.mainThread()
}
This is the qualifier:
#Qualifier
#Retention(AnnotationRetention.RUNTIME)
annotation class UiScheduler
And this is the dependent component:
#Component(
dependencies = [AppComponent::class],
modules = [EditEntryActivityModule::class, ViewModelModule::class]
)
#ActivityScope
interface EditEntryActivityComponent {
fun inject(editEntryActivity: EditEntryActivity)
fun inject(editEntryFragment: EditEntryFragment)
}
This is how the scheduler is injected in the fragment:
class EditEntryFragment : Fragment() {
#Inject #UiScheduler lateinit var uiScheduler: Scheduler
/* other stuff */
}
So why can the dependent component inject the bitmap cache, declared in the parent component, but not the UI scheduler? This is the error I get:
error: io.reactivex.Scheduler cannot be provided without an #Provides- or #Produces-annotated method.
io.reactivex.Scheduler is injected at
com.test.edit.EditEntryFragment.uiScheduler
com.test.edit.EditEntryFragment is injected at
com.test.edit.EditEntryActivityComponent.inject(arg0)
1 error
Using #field:UiScheduler in class EditEntryFragment
Try #Named annotition
#Inject #field:Named("UiScheduler") lateinit var uiScheduler: Scheduler
check out this issue
Related
i follow this tutorial Room Dependency Injection - MVVM To-Do List App with Flow and Architecture Components #4
Now I want to convert, a hole app that I have with Room, to this Clean Architecture.
In the tutorial Florian uses DI, to inject TaskDao into TaskViewModel, but I have a Repositories clases.
So I get to a point where the app is build without errors.
and this is my Repository:
class AnioRepository constructor(
private val agrotrackerApi: AgrotrackerApi
) {
val TAG = "AnioRepository"
//val anioDao: AnioDao
fun downloadAnios(): AniosResponse? {
GlobalScope.launch(Dispatchers.IO) {
val result = agrotrackerApi.getAnios()
if (result.isSuccessful) {
for (anio in result.body()!!){
Log.d(TAG, anio.toString())
}
}
}
return null
}
fun getAnios() {
//anioDao.getListAnios()
}
}
and this is my RepositoryModule:
#Module
#InstallIn(ApplicationComponent::class)
object RepositoryModule {
#Singleton
#Provides
fun providesAnioRepository( agrotrackerApi: AgrotrackerApi) : AnioRepository {
return AnioRepository(agrotrackerApi)
}
}
So I'm trying to add Dao class to the Repository Class, like this:
class AnioRepository constructor(
private val anioDao: AnioDao,
private val agrotrackerApi: AgrotrackerApi
) {
val TAG = "AnioRepository"
...
and then, change RepositoryModule, to match constructor...
...
fun providesAnioRepository( anioDao: AnioDao, agrotrackerApi: AgrotrackerApi) : AnioRepository
= AnioRepository(anioDao, agrotrackerApi)
...
but when I press Ctrl-F9, i get this error:
public abstract class ATMDatabase extends androidx.room.RoomDatabase {
^C:\pryectos\AndroidStudioProjects\ATMobileXKt\app\build\generated\source\kapt\debug\com\cse\atm\ATMApplication_HiltComponents.java:155:
error: [Dagger/MissingBinding] #com.cse.atm.di.ApplicationScope
kotlinx.coroutines.CoroutineScope cannot be provided without an
#Provides-annotated method. public abstract static class SingletonC
implements ATMApplication_GeneratedInjector,
^
#com.cse.atm.di.ApplicationScope kotlinx.coroutines.CoroutineScope is injected at
com.cse.atm.database.ATMDatabase.Callback(�, applicationScope)
com.cse.atm.database.ATMDatabase.Callback is injected at
com.cse.atm.di.AppModule.providesDatabase(�, callback)
com.cse.atm.database.ATMDatabase is injected at
com.cse.atm.di.AppModule.providesAnioDao(db)
com.cse.atm.database.AnioDao is injected at
com.cse.atm.di.RepositoryModule.providesAnioRepository(anioDao, �)
javax.inject.Provider<com.cse.atm.data.AnioRepository> is injected at
What means this error? What I'm missing ?
#ApplicationScope annotation is in another AppModule.kt, I dont' where is the problem.
Any help wil be appreciated!!
Best Regards
Your ATMDatabase.Callback is requesting a CoroutineScope with custom qualifier #ApplicationScope, but you are not providing such a CoroutineScope in any of your modules.
To provide a coroutine scope, the tutorial you linked adds this code around the 28-minute mark:
#Provides
#Singleton
fun provideApplicationScope() = CoroutineScope(SupervisorJob())
Since you are using the #ApplicationScope qualifier when requesting a coroutine scope, you will also need to add the qualifier to this #Provides method:
#Provides
#Singleton
#ApplicationScope
fun provideApplicationScope() = CoroutineScope(SupervisorJob())
Good day, I have experience in dagger2 but I'm trying to gain some experience in Hilt so I start with the demo application by migrating the demo from Dagger2 to Hilt. in my Dagger app, I have two components ApplicationComponent and ActivityComponent. ActivityComponent depends on the ApplicationComponent so I wrote ActivityComponent as following:-
#ActivityScope
#Component(
dependencies = [ApplicationComponent::class],
modules = [ActivityModule::class]
)
interface ActivityComponent {
fun inject(dummyActivity: DummyActivity)
fun inject(dummyActivity2: DummyActivity2)
}
so I tried to achieve the same behavior in Hilt. I know Hilt users never define or instantiate Dagger components directly. Instead, Hilt offers predefined components that are generated for us, and as the documentation explains "child component can have dependencies on any binding in an ancestor component"
so i thought i can do the following as ActivityComponent is a child of the SingletonComponent/ ApplicationComponent so ActivityComponent can access any bindings in the ApplicationComponent:-
#Module
#InstallIn(ApplicationComponent::class)
class ApplicationModule {
#Provides
#Singleton
fun provideDispatcherProvider(): DispatcherProvider = FlowDispatcherProvider()
#Provides
#Singleton
fun provideSharedPreferences(#ApplicationContext application: Application): SharedPreferences =
application.getSharedPreferences("project-prefs", Context.MODE_PRIVATE)
#Provides
#Singleton
fun provideNetworkHelper(#ApplicationContext application: Application): NetworkHelper = NetworkHelper(application)
#Provides
#Singleton
fun provideNetworkService(#ApplicationContext application: Application): NetworkService =
Networking.create(
BuildConfig.USERNAME,
BuildConfig.PASSWORD,
BuildConfig.BASE_URL,
application.cacheDir,
10 * 1024 * 1024 // 10MB
)
}
#Module
#InstallIn(ActivityComponent::class)
class ActivityModule {
#Provides
#ActivityScoped
fun provideMyHelper(
dispatcherProvider: DispatcherProvider,
networkHelper: NetworkHelper
):MyHelper = MyHelper(dispatcherProvider, networkHelper)
}
class MyHelper
(
private val dispatcherProvider: DispatcherProvider,
private val networkHelper: NetworkHelper
) {
}
#ActivityScoped
#AndroidEntryPoint
class DummyActivity(): AppCompatActivity() {
#Inject
lateinit var myhelper: MyHelper
}
but when I try to build this code I get:-
error: [Dagger/MissingBinding] #com.mypackage.ApplicationContext android.app.application cannot be provided without an #Provides-annotated method.
public abstract static class SingletonC implements MyApplication_GeneratedInjector,
^
#com.mypackage.ApplicationContext android.app.Application is injected at
mypackage.module.ApplicationModule.provideNetworkHelper(application)
com.mypackage.NetworkHelper is injected at
com.mypackage.ActivityModule.provideMyHelper(�, networkHelper)
com.mypackage.MyHelper is injected at
com.mypackage.DummyActivity.myhelper
com.mypackage.DummyActivity is injected at
com.mypackage.DummyActivity.myhelper
so what am I missing here?
Thanks in advance
Apparently you do not import the ApplicationContext qualifier correctly, it should be dagger.hilt.android.qualifiers.ApplicationContext.
I have built a Dynamic feature module sample with Fragments, sub components and dependent components based on plaid app, if you wish to check out here is the link. Now, i'm trying to convert it to Dagger Hilt using the official android document.
In core module which is the library module, app module and dynamic feature modules depend on
#Singleton
#Component(modules = [CoreModule::class])
interface CoreComponent {
/*
Provision methods to provide dependencies below to components that depends on
CoreComponent
*/
fun coreDependency(): CoreDependency
fun coreCameraDependency(): CoreCameraDependency
fun corePhotoDependency(): CorePhotoDependency
fun coreActivityDependency(): CoreActivityDependency
#Component.Factory
interface Factory {
fun create(#BindsInstance application: Application): CoreComponent
}
}
and it's module
#Module(includes = [CoreProvideModule::class])
abstract class CoreModule {
#Binds
abstract fun bindContext(application: Application): Context
}
#Module
object CoreProvideModule {
#Singleton
#Provides
fun provideCoreDependency(application: Application) = CoreDependency(application)
#ActivityScope
#Provides
fun provideCoreActivityDependency(context: Context) = CoreActivityDependency(context)
#Provides
fun provideCoreCameraDependency(): CoreCameraDependency = CoreCameraDependency()
#Provides
fun provideCorePhotoDependency(): CorePhotoDependency = CorePhotoDependency()
}
How is CoreComponent migrated? Do provision methods still stay and i only change
#Singleton
#DefineComponent
or
#Singleton
#DefineComponent(parent = ApplicationComponent.class)
for CoreModule i guess i only change
#EntryPoint
#InstallIn(CoreComponent::class)
or is this for adding provision methods in CoreComponent?
How do i create sub-component in app module?
If anyone has a sample with dynamic feature fragments and hilt, or tutorial to build, it would be more than welcome. I'm just working on it at the moment, if i figure it out i would post an answer
I finally figured it out.
For an app structure
FeatureCamera FeaturePhotos (Dynamic Feature Modules)
| | |
| ----App
| |
core(android-library)
Camera dynamic feature module dependencies from core module, Photo dynamic feature module dependencies from app.
First create a CoreModule in library module
#InstallIn(ApplicationComponent::class)
#Module
class CoreModule {
#Singleton
#Provides
fun provideCoreDependency(application: Application) = CoreDependency(application)
#Provides
fun provideCoreActivityDependency(context: Application) = CoreActivityDependency(context)
#Provides
fun provideCoreCameraDependency(): CoreCameraDependency = CoreCameraDependency()
#Provides
fun provideCorePhotoDependency(): CorePhotoDependency = CorePhotoDependency()
}
An interface with #EntryPoint is required to with provision methods defined in this interface, if you don't define a method for that dependency you cannot inject it even though there is a #Provides method for it. These are mock dependencies that take application or context as only parameter.
#EntryPoint
#InstallIn(ApplicationComponent::class)
interface CoreComponent {
/*
Provision methods to provide dependencies to components that depend on this component
*/
fun coreDependency(): CoreDependency
fun coreActivityDependency(): CoreActivityDependency
fun coreCameraDependency(): CoreCameraDependency
fun corePhotoDependency(): CorePhotoDependency
}
In camera dynamic feature module, create another module for the dependency based inside of this dynamic feature module.
#InstallIn(FragmentComponent::class)
#Module(includes = [CameraBindModule::class])
class CameraModule {
#Provides
fun provideCameraObject(context: Context) = CameraObject(context)
}
#InstallIn(FragmentComponent::class)
#Module
abstract class CameraBindModule {
#Binds
abstract fun bindContext(application: Application): Context
}
And component to inject dependencies to Fragments or Activities in this DFM.
#Component(
dependencies = [CoreComponent::class],
modules = [CameraModule::class]
)
interface CameraComponent {
fun inject(cameraFragment1: CameraFragment1)
fun inject(cameraFragment2: CameraFragment2)
fun inject(cameraActivity: CameraActivity)
#Component.Factory
interface Factory {
fun create(coreComponent: CoreComponent, #BindsInstance application: Application): CameraComponent
}
}
If injected to Activity call in `onCreate()`
DaggerCameraComponent.factory().create(
EntryPointAccessors.fromApplication(
applicationContext,
CoreComponent::class.java
),
application
)
.inject(this)
For injecting to Fragment call in `onCreate()`
DaggerCameraComponent.factory().create(
EntryPointAccessors.fromApplication(
requireActivity().applicationContext,
CoreComponent::class.java
),
requireActivity().application
)
.inject(this)
The trick is here to get dependency interface annotated with `#EntryPoint`
using `EntryPointAccessors.fromApplication()`
Also created Activity based dependencies in app module
**MainActivityModule.kt**
#InstallIn(ActivityComponent::class)
#Module(includes = [MainActivityBindModule::class])
class MainActivityModule {
#Provides
fun provideToastMaker(application: Application) = ToastMaker(application)
#ActivityScoped
#Provides
fun provideMainActivityObject(context: Context) = MainActivityObject(context)
}
#InstallIn(ActivityComponent::class)
#Module
abstract class MainActivityBindModule {
#Binds
abstract fun bindContext(application: Application): Context
}
And only intend to inject these dependencies to Photos dynamic feature module so named it as `PhotoDependencies`
#EntryPoint
#InstallIn(ActivityComponent::class)
interface PhotoModuleDependencies {
fun toastMaker(): ToastMaker
fun mainActivityObject(): MainActivityObject
}
In Photos dynamic feature module create dagger module named `PhotoModule`
#InstallIn(FragmentComponent::class)
#Module(includes = [PhotoBindModule::class])
class PhotoModule {
#Provides
fun providePhotoObject(application: Application): PhotoObject = PhotoObject(application)
}
#InstallIn(FragmentComponent::class)
#Module
abstract class PhotoBindModule {
#Binds
abstract fun bindContext(application: Application): Context
}
And component
#Component(
dependencies = [PhotoModuleDependencies::class],
modules = [PhotoModule::class]
)
interface PhotoComponent {
fun inject(photosFragment1: PhotoFragment1)
fun inject(photosFragment2: PhotoFragment2)
#Component.Factory
interface Factory {
fun create(photoModuleDependencies: PhotoModuleDependencies,
#BindsInstance application: Application): PhotoComponent
}
}
And inject to fragments with
DaggerPhotoComponent.factory().create(
EntryPointAccessors.fromActivity(
requireActivity(),
PhotoModuleDependencies::class.java
),
requireActivity().application
)
.inject(this)
The trick here is to get `EntryPointAccessors.fromActivity` instead of fromApplication.
You can check out [this link][1] if you wish to experiment yourself.
If you wish to add `ViewModel` to dynamic feature modules with hilt you can check out my answer [here][2].
[1]: https://github.com/SmartToolFactory/Dagger2-Tutorials/tree/master/Tutorial10-1DFM-DaggerHilt
[2]: https://stackoverflow.com/questions/63671489/how-to-create-viewmodel-in-dynamic-feature-module-with-dagger-hilt
I have been using Dagger Android for a while, and now I want to inject a MockModule for testing purposes, and I do not know how to get it. I have seen on some posts that they call something like:
DaggerAppComponent.builder()
.dataModule(myMockModule)
.create(this).inject(this)
But I have this configuration:
#Singleton
#Component(modules = [
AndroidSupportInjectionModule::class,
ActivitiesBuilderModule::class,
AppModule::class,
DataModule::class
])
internal interface AppComponent : AndroidInjector<CbvApplication> {
#Component.Builder
abstract class Builder : AndroidInjector.Builder<CbvApplication>()
}
and when I create the application, the DaggerAppComponent.Builder does not provides the setters for the individual modules. That is nice cause in the app code I do not have to worry about it, but on testing, I want to inject my mock objects through mockComponents / modules.
Anyone can point me in the right direction???
As far as I know, I am not sure we can mock the whole module all together, but we can mock dependencies provided by module.
I have achieved this like, I need to mock DataModule for UI Testing so I have created this TestDataModule and provided mocks for the dependency.
import com.nhaarman.mockito_kotlin.mock
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
#Module
class TestDataModule {
#Provides
#Singleton
fun providesDataRepository(): DataRepository {
return mock()
}
}
Here is TestApplicationModule
#Module
abstract class TestApplicationModule {
#Binds
abstract fun bindContext(application: Application): Context
}
And created TestApplicationComponent which will take the required modules
#Singleton
#Component(modules = [
(AndroidSupportInjectionModule::class),
(TestApplicationModule::class),
(UIModule::class),
(PresentationModule::class),
(TestDataModule::class)])
interface TestApplicationComponent {
// Here you can add additional direct mock
// classes to access them directly from component reference
fun dataRepository(): DataRepository
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): TestApplicationComponent.Builder
fun build(): TestApplicationComponent
}
fun inject(app: TestAppplication)
}
I had to test UI using Android JUnit Test runner and espresso, So I used UIModule and Presentation Module as it is (Cannot mock as wanted to test). So have to mock other dependencies which are not part of that UI Unit tests like DataRepository
You can add other modules like CacheModule and RemoteModules as mocks as they don't play any role in UI Unit testing.
Finally, Create DaggerTestApplicationComponent in TestApplication like this,
class TestApplication : Application(), HasActivityInjector {
#Inject lateinit var activityInjector: DispatchingAndroidInjector<Activity>
private lateinit var appComponent: TestApplicationComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerTestApplicationComponent.builder()
.application(this)
.build()
appComponent.inject(this)
}
override fun activityInjector(): AndroidInjector<Activity> = activityInjector
}
So now DaggerTestApplicationComponent will generate required dependency graph.
Note: It is required to stub all the methods called on mocked dependencies
In my app I have a component with Application scope (same as Singleton) that provides a ViewModel Factory, and a dependent component with Activity scope that injects the factory in a fragment.
The application component is defined as follows:
#Component(modules = [AppModule::class, /* other stuff */, ViewModelModule::class])
#ApplicationScope
interface AppComponent {
fun inject(app: Application)
/* other stuff */
val viewModelFactory: ViewModelFactory
}
The view model module is defined as follows:
#ApplicationScope
#Module
abstract class ViewModelModule {
#Binds
abstract fun bindViewModelFactory(viewModelFactory: ViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(DisplayEntryViewModelImpl::class)
abstract fun bindDisplayEntryViewModel(displayEntryViewModelImpl: DisplayEntryViewModelImpl): ViewModel
}
The activity scope component is defined as follows:
#Component(dependencies = [AppComponent::class], modules = [DisplayEntryActivityModule::class])
#ActivityScope
interface DisplayEntryActivityComponent {
fun inject(displayEntryActivity: DisplayEntryActivity)
fun inject(displayEntryFragment: DisplayEntryFragment)
}
When I try to inject the viewmodel factory in the fragment I get this error:
error: android.arch.lifecycle.ViewModelProvider.Factory cannot be provided without an #Provides- or #Produces-annotated method.
If I update the activity component to include the view model module, like this
#Component(dependencies = [AppComponent::class], modules = [DisplayEntryActivityModule::class, ViewModelModule::class])
#ActivityScope
interface DisplayEntryActivityComponent {
fun inject(displayEntryActivity: DisplayEntryActivity)
fun inject(displayEntryFragment: DisplayEntryFragment)
}
Then it compiles. My understanding is that dependent components have access to the injected members from the parent component if the parent component explicitly provides those members, as I do here with the
val viewModelFactory: ViewModelFactory
so why do I still need to provide the viewmodel module in the activity scope component?
When using dependencies, dagger will use that component to inject member.
Parent component must explicitly declare objects which can be used in child components.
#Component(modules = [AppModule::class, /* other stuff */, ViewModelModule::class])
#ApplicationScope
interface AppComponent {
fun inject(app: Application)
fun viewModelFactory(): ViewModelProvider.Factory
fun viewModel(): ViewModel
}
You can take a look at this article:
https://proandroiddev.com/dagger-2-part-ii-custom-scopes-component-dependencies-subcomponents-697c1fa1cfc