I have a Component to provide dependencies for Data Input:
#Component(modules = [DataInputModule::class])
interface DataInputComponent {
fun inject(activity: DataInputActivity)
fun factory(): Factory
#Component.Factory
interface Factory {
fun create(#BindsInstance activity: DataInputActivity, module: DataInputModule): DataInputComponent
}
}
With the module:
#Module
class DataInputModule(private val activity: DataInputActivity) {
#Provides
fun provideDataInputViewModel(repo: MyRepository, timestampProvider: TimestampProvider) =
DefaultDataInputViewModel(repo, timestampProvider)
#Provides
fun provideTimestampProvider(): TimestampProvider = DefaultTimestampProvider()
#Provides
fun provideDateTimeDialogFactory(): DateTimeDialogFactory = DateTimeDialogFactory(activity)
}
My application's component looks like so:
#Singleton
#Component(
modules = [
DataListModule::class
]
)
interface AppComponent : AndroidInjector<MyApplication> {
#Component.Factory
interface Factory {
fun create(#BindsInstance app: MyApplication): AppComponent
}
}
with the module to provide the trusty Room database and the repository.
When trying to build I get the following error:
D:\Applications\AndroidStudioProjects\MyApp\app\build\tmp\kapt3\stubs\debug\com\example\myapp\di\component\DataInputComponent.java:7:
error: [Dagger/MissingBinding]
com.example.myapp.di.component.DataInputComponent.Factory cannot
be provided without an #Provides-annotated method. public abstract
interface DataInputComponent {
^
com.example.myapp.di.component.DataInputComponent.Factory is provided at
com.example.myapp.di.component.DataInputComponent.factory()
I cannot add a #Provides to the #Component because it is not a module. I looked for docs online but found no example of anyone "provides" annotating and providing a #Factory. So what could cause the issue here?
Related
I'm trying to migrate my project to Dagger Hilt and facing an issue with missing binding. I was following the Googles codelab to achieve this.
This is the place where the build fails:
error: [Dagger/MissingBinding] java.util.Map<java.lang.String,javax.inject.Provider<dagger.android.AndroidInjector.Factory<?>>> cannot be provided without an #Provides-annotated method.
public abstract static class ApplicationC implements WhatToCookApp_GeneratedInjector,
^
java.util.Map<java.lang.String,javax.inject.Provider<dagger.android.AndroidInjector.Factory<?>>> is injected at
dagger.android.DispatchingAndroidInjector(�, injectorFactoriesWithStringKeys)
dagger.android.DispatchingAndroidInjector<java.lang.Object> is injected at
dagger.android.support.DaggerAppCompatActivity.androidInjector
at.bwappsandmore.whattocook.MainActivity is injected at
dagger.android.AndroidInjector.inject(T) [at.bwappsandmore.whattocook.WhatToCookApp_HiltComponents.ApplicationC ? at.bwappsandmore.whattocook.di.ActivityModule_InjectMainActivity.MainActivitySubcomponent]
It is also requested at:
dagger.android.DispatchingAndroidInjector(�, injectorFactoriesWithStringKeys)
The following other entry points also depend on it:
These are the relevant parts of the project:
#HiltAndroidApp
open class WhatToCookApp : Application() {
val appComponent: AppComponent by lazy {
initializeComponent()
}
open fun initializeComponent(): AppComponent {
return DaggerAppComponent.factory().create(applicationContext)
}
}
The AppComponent:
#Singleton
#Component(
modules = [AppModule::class,
ActivityModule::class,
AndroidSupportInjectionModule::class]
)
interface AppComponent : AndroidInjector<WhatToCookApp> {
#Component.Factory
interface Factory {
fun create(#BindsInstance appContext: Context): AppComponent
}
}
The AppModule:
#InstallIn(ApplicationComponent::class)
#Module
class AppModule {
#Provides
fun provideDB(#ApplicationContext context: Context): AppDatabase {
return AppDatabase.getDatabase(context)
}
#Provides
fun provideDAO(app: AppDatabase): WhatToCookDao {
return app.whatToCookDao()
}
#Provides
fun provideAppRepository(dao: WhatToCookDao): AppRepository{
return AppRepository(dao)
}
#Provides
fun provideSharedPreferences(#ApplicationContext context: Context): SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(context)
}
}
The ApplicationModulde:
#InstallIn(ApplicationComponent::class)
#Module
interface ActivityModule {
#ActivityScope
#ContributesAndroidInjector(modules = [ViewModelModule::class])
fun injectMainActivity(): MainActivity
}
The ViewModelModule:
#InstallIn(ApplicationComponent::class)
#Module
abstract class ViewModelModule {
companion object{
#Provides
fun providesSharedViewModel (activity: MainActivity) : SharedViewModel = activity.viewModel
}
}
The ActivityScope:
#Scope
#Retention(AnnotationRetention.RUNTIME)
annotation class ActivityScope
I realise that I have to use the #Provides annotation for the AndroidInjector, but I don't know where and how. Any help is appreciated.
Thank you so much in advance.
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 a app with 3 different modules: app(main),api and repository
My app module depends on repository module, that depends on api module:
app -(depends)-> repository -(depends)-> api
I would like to use Dagger2 in all 3 modules to inject dependencies in each other without exposing theses dependencies to the modules that doesn't need to see it.
Basically the api module will provide service related classes to communicate with the API
The repository module will use the api classes to coordinate the call of the resources
The app module will use only the repository classes directly, without knowing anything of the api module
Here are how my dagger modules/components are structured:
ApiModule:
#Component(modules = [ApiModule::class])
interface ApiComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun requestApiModule(apiModule: ApiModule): Builder
fun build(): ApiComponent
}
}
#Module
class ApiModule {
#Provides
#Reusable
fun provideApiClient(apiConfig: ApiConfig) = ApiClient(apiConfig)
#Provides
#Reusable
fun provideConsumerService(appConsumerService: AppConsumerService): ConsumerService = appConsumerService
#Provides
#Reusable
fun provideParkingService(appParkingService: AppParkingService): ParkingService = appParkingService
}
RepositoryModule:
#Component(
modules = [RepositoryModule::class],
dependencies = [ApiComponent::class]
)
interface RepositoryComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun requestRepositoryModule(repositoryModule: RepositoryModule): Builder
fun apiComponent(apiComponent: ApiComponent): Builder
fun build(): RepositoryComponent
}
}
#Module
class RepositoryModule {
#Provides
#Reusable
fun provideLocalStorage(application: Application) = LocalStorage(application.applicationContext)
#Provides
#Reusable
fun provideLocalSession(application: Application) = LocalSession(application.applicationContext)
#Provides
#Reusable
fun provideApiConfig(apiRepositoryConfig: ApiRepositoryConfig): ApiConfig = apiRepositoryConfig
#Provides
#Reusable
fun provideConsumerRepository(appConsumerRepository: AppConsumerRepository): ConsumerRepository =
appConsumerRepository
#Provides
#Reusable
fun provideParkingRepository(appParkingRepository: AppParkingRepository): ParkingRepository = appParkingRepository
}
AppModule:
#Singleton
#Component(
modules = [ActivityModule::class, ViewModelModule::class, AndroidInjectionModule::class],
dependencies = [RepositoryComponent::class]
)
interface ApplicationComponent : AndroidInjector<CustomApplication> {
override fun inject(customApplication: CustomApplication)
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun repositoryComponent(repositoryComponent: RepositoryComponent): Builder
fun build(): ApplicationComponent
}
}
After trying to run the project I'm seeing the current errors in the build output
error: cannot access ApiComponent class file for com.spaces.api.module.ApiComponent not found Consult the following stack trace for details
error: [ComponentProcessor:MiscError] 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.
I'm not able to build my app due to following error.
#Component.Builder is missing setters for required modules or components
I'm using
implementation "androidx.room:room-runtime:2.1.0"
kapt "androidx.room:room-compiler:2.1.0"
My apps RoomModul looks like this:
#Module
class RoomModule(application: Application) {
#Singleton
private var logDatabase : LogDatabase =
Room.databaseBuilder(application, LogDatabase::class.java, "log-db").build()
#Singleton
#Provides
fun providesLogDatabase() : LogDatabase {
return logDatabase
}
#Singleton
#Provides
fun providesLogDao() : LogDao {
return logDatabase.getLogDao()
}
#Singleton
#Provides
fun providesLogRepository(logDao: LogDao) : LogRepository {
return LogDataSource(logDao)
}
}
And my AppComponent looks like this:
#Singleton
#Component(
modules = [
AndroidInjectionModule::class,
AppModule::class,
MainActivityModule::class,
RoomModule::class
])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(logApplication: LogApplication)
}
So my RoomModule needs the application to provide the room database. But I keep getting the error.
I thought that #BindsInstance should provide the application instance to my modules. I also tried to remove the constructor from my RoomModule with no success. Please let me know if I can provide more information.
Try to use this
#Module
class RoomModule()
{
#Singleton
#Provides
private var logDatabase(application: Application) : LogDatabase =
Room.databaseBuilder(application, LogDatabase::class.java, "log-db").build()
----
}
And Remove this line ---> fun inject(logApplication: LogApplication)
from AppComponent
It is confusing that all web search results seem to use slightly different versions of Dagger or different approaches. I followed the example which claims that is the better "new way". (https://proandroiddev.com/exploring-the-new-dagger-android-module-9eb6075f1a46) The full sample source code is here (https://github.com/jshvarts/DaggerAndroidKotlinSampleApp).
Now, I want to know how a non-activity/fragment class could be provided with a Context. So, I added a simple class like this,
class Sample2 #Inject constructor (var app: Application)
{
fun print()
{
Log.d("sample2", app.packageName);
}
}
But even though the sample project had AppModule and AppComponent, the compilation failed, because app could not be provided.
I have searched the web and found this method (https://github.com/google/dagger/issues/832). I followed that code and modified the sample's AppModule and AppComponent like this,
#Module(includes = [AndroidInjectionModule::class])
abstract class AppModule {
#Binds
abstract fun application(app: App):Application;
#Singleton
#Provides
fun providesCommonHelloService() = CommonHelloService()
}
#Singleton
#Component(modules = [AndroidInjectionModule::class,
AppModule::class, ActivitiesModule::class])
interface AppComponent:AndroidInjector<App>
{
#Component.Builder
abstract class Builder:AndroidInjector.Builder<App>(){}
}
class App : Application(), HasActivityInjector {
#Inject
lateinit var activityInjector: DispatchingAndroidInjector<Activity>
override fun onCreate()
{
super.onCreate()
DaggerAppComponent.builder().create(this).inject(this)
}
override fun activityInjector(): AndroidInjector<Activity> = activityInjector
}
But the, I get the following compilation error.
AppModule.java:7: error: A #Module may not contain both non-static #Provides methods and abstract #Binds or #Multibinds declarations
public abstract class AppModule {
Again, as I have said in the beginning, the Dagger examples on the Internet are all slightly different, I do not know how to take two features from two examples.
It is better to separate #Binds and #Provides, you can create a component class:
#Singleton
#Component(
modules = [(AppModule::class)]
)
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(app: Application): Builder
fun build(): AppComponent
}
}
then an AppModule class for all your #Provides
#Module
class AppModule() {
#Singleton
#Provides
fun providesCommonHelloService() = CommonHelloService()
}