In :app module
#Singleton
#Component(modules = [AppModule::class])
interface AppComponent {
#Component.Factory
interface Factory {
fun create(#BindsInstance context: Context): AppComponent
}
}
#Module
class AppModule {
#Singleton
#Provides
fun provideA() = A()
}
In dynamic feature module
#Component(
dependencies = [AppComponent::class],
modules = [FeatureModule::class]
)
interface FeatureComponent{
#Component.Factory
interface Factory {
fun create(appComponent: AppComponent): FeatureComponent
}
fun inject(fragment: HomeFragment)
}
#Module
class FeatureModule {
}
In HomeFragment or HomeViewModel, I can't inject object A (provided in AppModule in AppComponent).
How to resolve it?
Thanks.
When you use Dagger's component dependencies (as you are doing),
when FeatureComponent depends on AppComponent,
then AppComponent needs to expose the dependencies using provision functions so that FeatureComponent can inject them.
Provision functions are just functions in the component interface, for example:
#Singleton
#Component(modules = [AppModule::class])
interface AppComponent {
fun provideA(): A // <--- this is a provision function, you need to add this to expose "A" from AppComponent
#Component.Factory
interface Factory {
fun create(#BindsInstance context: Context): AppComponent
}
}
Just like other functions in Dagger Modules, names of these functions don't matter. However their return types and qualifiers (if there is any) matter.
You can also extract these provision functions to other interfaces and make your AppComponent extend those other interfaces in order to keep your code base organised, just like in this sample project.
From the docs:
When a type is used as a component dependency, each provision method
on the dependency is bound as a provider. Note that only the bindings
exposed as provision methods are available through component
dependencies.
Related
Background
I am trying to use dagger in a multi module setup. One of my aim is to reduce the number of components being used. So basically aiming for 1 component per feature module.
Setup core->app->feature
Problem
Dagger fails with the exception A binding with matching key exists in component: which refers to that I have bound a dependency somewhere in my entire object graph but it cannot be reached.
But for my scenario I am creating the sub-component in my activity and calling inject to make sure the component has the access to my activity. This atleast in my understanding should be accessible but it's still not able to provide the dependency of my viewmodel.
Here is the sample/multi-module in case someone wants to try out.
Stacktrace
/Users/feature1/build/**/FeatureComponent.java:8: error: [Dagger/MissingBinding]
com.**.FeatureActivity cannot be provided without an #Inject constructor or an #Provides-annotated
method. This type supports members injection but cannot be implicitly provided.
public abstract interface FeatureComponent {
^
A binding with matching key exists in component: com.**.FeatureComponent
com.**.FeatureActivity is injected at
com.**.FeatureModule.provideVM(activity)
com.**.FeatureViewModel is injected at
com.**.FeatureActivity.vm
com.**.FeatureActivity is injected at
com.**.FeatureComponent.inject(com.**.FeatureActivity)
AppComponent
#AppScope
#Component(dependencies = [CoreComponent::class])
interface AppComponent {
fun inject(app: MainApp)
#Component.Factory
interface Factory {
fun create(
coreComponent: CoreComponent
): AppComponent
}
}
CoreComponent
#Singleton
#Component
interface CoreComponent {
fun providerContext(): Context
#Component.Factory
interface Factory {
fun create(
#BindsInstance applicationContext: Context
): CoreComponent
}
}
FeatureComponent
#Component(
modules = [FeatureModule::class],
dependencies = [CoreComponent::class]
)
#FeatureScope
interface FeatureComponent {
// Planning to use this component as a target dependency for the module.
fun inject(activity: FeatureActivity)
}
Feature Module
#Module
class FeatureModule {
#Provides
fun provideVM(activity: FeatureActivity): FeatureViewModel {
val vm by activity.scopedComponent {
FeatureViewModel()
}
return vm
}
}
Feature VM
class FeatureViewModel #Inject constructor(): ViewModel()
Since I'm using activity to provide my viewModel I will have to use #BindsInstance to bind the instance of any activity or fragment that I want to inject.
In short if I change my feature component to the following code it starts to work where I bind the instance of the activity at the creation of the component.
PS: If anyone knows a better to inject the fragment at later stage with just using one component, please feel free to improve this answer.
FeatureComponent
#Component(
modules = [FeatureModule::class],
dependencies = [CoreComponent::class]
)
#FeatureScope
interface FeatureComponent {
fun inject(activity: FeatureActivity)
#Component.Factory
interface Factory {
fun create(
#BindsInstance applicationContext: FeatureActivity,
coreComponent: CoreComponent,
): FeatureComponent
}
}
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 two project modules (with this I mean two android modules with their own gradle, manifest, etc.. Not the Dagger module). I call them MyAppCore and MyApp. MyAppCore has the logic around the database access and network access. MyApp has all the UI (activities, views, modelviews, etc).
I'm using dagger 2 to inject dependencies of different components in my project, however, I'm having trouble to link both modules together.
MyApp and MyAppCore have their own AppComponent, where MyApp's provides the ViewModel factories and MyAppCore's provides the ones for database and network access (examples below).
I'm not sure how to link both AppComponent (or Applications) so that database and network accesses can be provided in MyApp. Here's what I have so far:
MyAppCore module
CoreApp
open class CoreApp : Application() {
val appComponent: AppComponent by lazy {
initializeComponent()
}
open fun initializeComponent(): AppComponent {
return DaggerAppComponent.builder()
.build()
}
}
AppComponent
#Singleton
#Component(modules = [AppModule::class])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
}
AppModule
#Module
class AppModule {
#Singleton
#Provides
fun provideDb(app: Application) = MyDb.getInstance(app)
#Singleton
#Provides
fun provideCommentDao(db: MyDb) = db.commentDao()
}
CommentRep (to access the CommentDao)
#Singleton
class CommentRep #Inject constructor(private val dao: CommentDao) {
fun saveComment(comment: Comment){
dao.insert(comment)
}
}
MyAppCore also has the Room database implementation called MyDb and the interface CommentDao (I don't think I need to add this code in this question).
MyApp module
MyApp
open class MyApp : Application(), DaggerComponentProvider {
override val appComponent: AppComponent by lazy {
initializeComponent()
}
open fun initializeComponent(): AppComponent {
return DaggerAppComponent.builder()
.applicationContext(applicationContext)
.build()
}
}
DaggerComponentProvider
interface DaggerComponentProvider {
val appComponent: AppComponent
}
val Activity.injector get() = (application as DaggerComponentProvider).appComponent
AppComponent
#Singleton
#Component
interface AppComponent{
#Component.Builder
interface Builder {
#BindsInstance
fun applicationContext(applicationContext: Context): Builder
fun build(): AppComponent
}
fun commentsViewModelFactory(): ViewModelFactory<CommentsViewModel>
}
ViewModelFactory
class ViewModelFactory<VM : ViewModel> #Inject constructor(
private val viewModel: Provider<VM>
) : ViewModelProvider.Factory {
#Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>) = viewModel.get() as T
}
CommentsViewModel
class CommentsViewModel #Inject constructor(private val repository: CommentRep) : ViewModel() {
fun saveComment(comment: Comment){
repository.saveComment(comment)
}
}
And then my Activities which inject the VM but I don't think they are necessary to include their code in this question.
Of course, if I compile my project as this, the graph from MyAppCore is not generated and hence I get the error that I need to provide CommentDao because it's required by CommentRep which is used by CommentsViewModel. I guess MyAppCore application class is overridden by MyApp application class and hence MyAppCore's AppComponent is never instantiated and hence never added all the core's injections in my graph. How do I solve this problem? Thanks in advance!
Just use one component. First, add MyAppCore module to MyApp's gradle dependencies. Then, Let MyApp module provide the component which will include all the dagger modules in both project modules.
You could also change MyAppCore module to a library module since you only need one application module. In your build.gradle file, replace:
apply plugin: 'com.android.application'
to
apply plugin: 'com.android.library'
To add MyAppCore module to MyApp module's dependencies, add:
implementation project(":myappcore")
And for your component in MyApp module:
#Singleton
#Component(modules=[MyAppCoreModule::class, MyAppModule::class])
interface AppComponent {
...
}
So basically, you only need to provide modules in MyAppCore.
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
I'm trying to create UserScope with https://google.github.io/dagger/android I have #Singleton, #ActivityScope, #FragmentScope and #UserScope.
AppComponent
#Singleton
#Component(
modules = [
AndroidSupportInjectionModule::class,
ActivityBindingModule::class,
AppModule::class,
ApiModule::class
]
)
interface AppComponent {
fun inject(application: Application)
fun createUserComponent(): UserComponent.Builder
#Component.Builder
interface Builder {
#BindsInstance
fun application(app: Application): Builder
fun apiModule(module: ApiModule): Builder
fun build(): AppComponent
}
}
UserComponent:
#UserScope
#Subcomponent(
modules = [UserModule::class]
)
interface UserComponent {
#Subcomponent.Builder
interface Builder {
#BindsInstance
fun user(user: User): Builder
fun build(): UserComponent
}
}
UserModule:
#Module
class UserModule {
#UserScope
#Provides
fun provideUser(user: User): User = user
}
Here is how I am creating UserComponent after successfull login:
private fun createSession(user: User) {
userComponent = appComponent.createUserComponent().user(user).build()
}
And also I have UserManager which is triggering an issue with User injection constructor param
#UserScope
class SomeManager #Inject constructor(
private val apiService: ApiService,
private val user: User
) {}
Error message: Error:[dagger.android.AndroidInjector.inject(T)]
domain.model.authorize.User cannot be provided without an #Inject
constructor or from an #Provides-annotated method.
di.modules.MainActivityBindingModule_ContributeUserFragment.UserFragmentSubcomponent
scoped with #di.scopes.FragmentScope may not reference bindings with
different scopes: #di.scopes.UserScope class
domain.managers.SomeManager
I want to create #UserScope to manage related ApiManagers lifecycle
UPD
#Module
class UserFragmentModule {
#Provides
#FragmentScope
fun provideViewModelFactory(someModule: SomeModule) = UserFragmentViewModelFactory(someModule)
}
ContributesAndroidInjector logic:
#Module
interface ActivityBindingModule {
#ActivityScope
#ContributesAndroidInjector(modules = [SplashModule::class])
fun contributeSplashActivity(): SplashActivity
#ActivityScope
#ContributesAndroidInjector(modules = [SignInModule::class])
fun contributeAuthorizeActivity(): Activity
#ActivityScope
#ContributesAndroidInjector(modules = [MainModule::class])
fun contributeMainActivity(): MainActivity
}
#Module(includes = [MainActivityBindingModule::class])
class MainModule
#Module
interface MainActivityBindingModule {
#FragmentScope
#ContributesAndroidInjector(modules = [UserFragmentModule::class])
fun contributeUserFragment(): UserFragment
}
You are trying to inject a User instance that is provided only in the #UserScope in #FragmentScope.
Basically, the rule is whenever you need a User injected - you need to be in the #UserScope.
You need to change your #FragmentScope to #UserScope annotation in all places like that.
From what you posted I believe it is here:
#Module
class UserFragmentModule {
#Provides
#UserScope
fun provideViewModelFactory(someModule: SomeModule) = UserFragmentViewModelFactory(someModule)
}
And you'll need to move your UserFragmentModule to the UserComponent:
#UserScope
#Subcomponent(
modules = [UserModule::class, UserFragmentModule::class]
)
interface UserComponent {
And you'll also need to inject with application.userComponent into your UserFragment
Here is a good article about subcomponents. I'd recommend you to read it to gain full understanding.
Another option, I'm not sure about this one, but maybe you can just inject your UserFragment with your application.userComponent.
I think your problem is wrong use of scopes.
Scopes help us to handle injected item life-cycle to prevent keep un-needed object in whole app life cycle.
and take look at this image:
Your modules should be in same scope that your component is.
for example in your AppComponent, you have ActivityBindingModule that have ActivityScope. why you do this?
I think it is better to have a activityComopnent that have dependency toappComponent. If your activityComponent had some dependency in upper scoped component (appComponent), first you had add dependency of that component. (dependencies= [ AppComponent::class]). Second, you had to expose needed dependency in appComponent with a method that return needed dependency object. Finally in wireUpping you should call appComponent as call activity modules in Dagger wire upping. ( I will show this in a example)
So all you need is activityComponent like this:
#ActivityScope
#Component(modules = [ActivityBindingModule::class],dependencies= [ AppComponent::class])
interface activityComponent {
.
.
.
This is my example written in java that shows how connect appComponent and activityComponent:
#Singleton
#Component(modules = {ApplicationModule.class ,ServicesModule.class})
public interface ApplicationComponent {
void inject(ImicoApplication imicoApplication);
// exposed for child-components.
Context getContext();
Application getApplication();
CompanyService getCompanyService();
CategoryService getCategoryService();
RatingService getRatingService();
NewsService getNewsService();
AuthorizationManager getAuthorizationManager();
}
And this is activityComponent:
#ActivityScope
#Component(dependencies = {ApplicationComponent.class},modules = {SearchActivityModule.class})
public interface SearchserviceComponent {
void inject (SearchFragment searchFragment);
}
And in SearchFragment do this for wire-up:
DaggerSearchserviceComponent.builder()
.applicationComponent(((ImicoApplication) getActivity().getApplication()).getApplicationComponent())
.searchActivityModule(new SearchActivityModule(this)).build().inject(this);
If in SearchFragment i need CompanyService just inject it and it is provided and exposed by applicationComponent.