AppComponent:
#PerApplication
#Component(modules = arrayOf(AppModule::class))
interface AppComponent {
}
AppModule:
#Module(subcomponents = arrayOf(ActivityComponent::class))
class AppModule(private val context: Context) {
#Provides #PerApplication
fun provideContext() = context
#Provides #PerApplication
fun provideSharedPreferences(context: Context)
= context.getSharedPreferences("${context.packageName}.preferences", Context.MODE_PRIVATE)
#Provides #PerApplication
fun provideMoshi() = Moshi.Builder().build()
#Provides #PerApplication
fun provideRetrofit(moshi: Moshi): Retrofit {
return Retrofit.Builder()
.baseUrl("https://reqres.in")
.addConverterFactory(MoshiConverterFactory.create(moshi))
.addCallAdapterFactory(RxJavaCallAdapterFactory.createWithScheduler(Schedulers.io()))
.build()
}
}
ActivityComponent:
#PerActivity
#Subcomponent(modules = arrayOf(ActivityModule::class))
interface ActivityComponent {
fun inject(activity: MainActivity)
#Subcomponent.Builder
interface Builder {
fun activityModule(module: ActivityModule): Builder
fun build(): ActivityComponent
}
}
ActivityModule:
#Module
class ActivityModule(private val activity: FragmentActivity) {
#Provides #PerActivity
fun provideActivity() = activity
#Provides #PerActivity
fun provideTestApi(retrofit: Retrofit) = retrofit.create(TestApi::class.java)
}
How do I get a Builder instance of ActivityComponent into MainActivity?
Activities are created by Dagger, so I can't inject a provider into the constructor like the example given in the official docs.
I tried to use the pre dagger-2.7 way of creating subcomponents by add the following line to AppComponent:
fun activityComponent(module: ActivityModule): ActivityComponent
and in MainActivity:
(application as App).appComponent.activityComponent(ActivityModule(this)).inject(this)
which gave me:
Error:[com.example.injection.ActivityComponent.inject(com.example.modules.main.MainActivity)] com.example.api.TestApi cannot be provided without an #Provides-annotated method.
Related
I'm learning dagger2 with a module architecture. And I think, something is not clear to me, for example
in module utilites i have di package
class UtilsComponent
#Component(modules = [UtilsModule::class])
interface UtilsComponent {
fun getResourceProvider() : IResourceProvider
fun getNetworkProvider(): INetworkProvider
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): UtilsComponent.Builder
fun build(): UtilsComponent
}
}
#Module
abstract class UtilsModule {
#Binds
abstract fun bindContext(application: Application): Context
#Module
companion object {
#Provides
#JvmStatic
fun bindResourceProvider(context: Context): IResourceProvider {
return ResourceProvider(context = context)
}
#Provides
#JvmStatic
fun bindNetworkProvider(context: Context): INetworkProvider {
return NetworkProvider(context = context)
}
}
}
then in app package in AppComponent i included all modules
#Component(
dependencies = [UtilsComponent::class],
modules = [
AndroidInjectionModule::class,
ActivityBindingModule::class,
MainModule::class // test module
]
)
#AppScope
interface AppComponent: AndroidInjector<App> {
// inject to ...
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): AppComponent.Builder
fun utilsComponent(utilsComponent: UtilsComponent): AppComponent.Builder
fun build(): AppComponent
}
}
In the app component, I have MainModule - this is my test module, so this module looks like this
#Module
class MainModule {
#Provides
fun getMainPresenter(networkProvider: NetworkProvider): MainPresenter {
return MainPresenter(networkProvider)
}
}
when I run the app, I have an error
[Dagger/MissingBinding] com.example.utilities.di.UtilsModule cannot be
provided without an #Provides-annotated method. public abstract
interface AppComponent extends
dagger.android.AndroidInjector<com.example.testmoduleapp.App> {
^
com.example.utilities.di.UtilsModule is injected at
com.example.testmoduleapp.di.modules.MainModule.getMainPresenter(utilsModule)
com.example.testmoduleapp.ui.activities.main.MainPresenter is injected at
com.example.testmoduleapp.ui.activities.main.MainActivity.mainPresenter
com.example.testmoduleapp.ui.activities.main.MainActivity is injected at
dagger.android.AndroidInjector.inject(T) [com.example.testmoduleapp.di.AppComponent →
com.example.testmoduleapp.di.modules.ActivityBindingModule_MainActivity.MainActivitySubcomponent]
I understand that the error is because I have not a module witch return NetworkProvider object, but I can't understand how I can get this object from UtilsModule
also in App
#Inject
lateinit var androidInjector : DispatchingAndroidInjector<Any>
override fun androidInjector(): AndroidInjector<Any> = androidInjector
companion object{
lateinit var appComponent: AppComponent
}
override fun onCreate() {
super.onCreate()
initializeDagger()
}
private fun initializeDagger() {
appComponent = DaggerAppComponent
.builder()
.application(this)
.utilsComponent(provideUtilsComponent())
.build()
}
private fun provideUtilsComponent(): UtilsComponent {
return DaggerUtilsComponent
.builder()
.application(this)
.build()
}
In Dependencies Graph You already provide INetworkProvider so when you need NetworkProvider it means Dagger can not be resolved. Change to this, don't forget to change constructor MainPresenter to INetworkProvider
#Provides
fun getMainPresenter(networkProvider: INetworkProvider): MainPresenter {
return MainPresenter(networkProvider)
}
but i cant understand how i can get this object from UtilsModule
For your question, in UtilComponent you already exposed getNetworkProvider() it means any Componenent dependencies to UtilsComponent can be get it.
I try to call API with Retrofit and Dagger 2 and in my case the DataModel and it's module are injected fine, but not the module of API. I'm sure I miss something, but do not understand what. Here is my Component:
#Singleton
#Component(modules = [SMModule::class, SMNetworkModule::class])
interface SMComponent {
fun inject(fragment: SMListFragment)
}
Modules:
#Module
class SMModule {
#Provides
fun provideSMModel(): SMListDataModel {
return SMListDataModel()
}
}
#Module
object SMNetworkModule {
#JvmStatic
#Singleton
#Provides
fun provideRetrofit(): Retrofit {
val okHttpBuilder = OkHttpClient.Builder()
if (BuildConfig.DEBUG) {
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
okHttpBuilder.addInterceptor(httpLoggingInterceptor)
}
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpBuilder.build())
.build()
}
#JvmStatic
#Singleton
#Provides
fun provideSMInterface(retrofit: Retrofit): SMInterface =
retrofit.create(SMInterface::class.java)
}
DataModel:
class SMListDataModel #Inject constructor() {
#Inject
lateinit var smApi: SMAPI
override fun loadData(): DataResponse {
return smApi.getData().blockingGet()
}
}
my API:
#Singleton
class SMAPI #Inject constructor() {
#Inject
lateinit var smInterface: SMInterface
fun getData(): Single<DataResponse> {
return smInterface.getData()
}
}
and here I inject my Fragment and call DataModel:
#JvmField
#Inject
var dataModel: StocksListDataModel? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DaggerSMComponent.create().inject(this)
}
override fun onResume() {
super.onResume()
val response = dataModel?.loadData()
}
and I get an error:
UninitializedPropertyAccessException: lateinit property smApi has not been initialized
inside of my SMListDataModel.
I looked at this question and there I didn't see the explicit constructor for the API and it works for them. But not for me.
I use this and it works well. (provide Room & Retrofit).
i hope it's be useful
RemoteModule class :
#Module(includes = [AppModule::class])
class RemoteModule {
#Provides
#Singleton
fun provideArticleDatabase(application: App): RoomDataBase? {
return Room.databaseBuilder(
application.applicationContext,
RoomDataBase::class.java,
"fandogh"
)
.build()
}
#Provides
fun provideArticleDao(#Nullable articleDatabase: RoomDataBase): ILocalDataSource? {
return articleDatabase.roomDao()
}
#Provides
#ApplicationScope
fun getApiInterface(retroFit: Retrofit): ApiService {
return retroFit.create(ApiService::class.java)
}
#Provides
#ApplicationScope
fun getRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
// .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build()
}
#Provides
#ApplicationScope
fun getOkHttpCleint(httpLoggingInterceptor: HttpLoggingInterceptor, myAuthHeaderInterceptor: MyAuthHeaderInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.readTimeout(30, TimeUnit.SECONDS)
.connectTimeout(30, TimeUnit.SECONDS)
.callTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
// .addInterceptor {
// var request = it.request().newBuilder().addHeader("authorization", Constants.AUTHORIZATION_HEADER).build()
// return#addInterceptor it.proceed(request)
// }
.addInterceptor(myAuthHeaderInterceptor)
.addInterceptor(httpLoggingInterceptor)
.build()
}
#Provides
#ApplicationScope
fun getHttpLoggingInterceptor(): HttpLoggingInterceptor {
val httpLoggingInterceptor = HttpLoggingInterceptor()
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
return httpLoggingInterceptor
}
#Singleton
#Provides
fun getAuthHeaderInterceptor(sharedPrefrences: SharedPreferences): MyAuthHeaderInterceptor {
return MyAuthHeaderInterceptor(sharedPrefrences)
}
}
ViewModelModule :
#Module
abstract class ViewModelModule {
#Binds
abstract fun bindViewModelFactory(factory: DaggerViewModelFactory): ViewModelProvider.Factory // this needs to be only one for whole app (therefore marked as `#Singleton`)
#Binds
#IntoMap
#ViewModelKey(LoginViewModel::class)
abstract fun loginViewModel(viewModel: LoginViewModel): ViewModel
#Binds
#IntoMap
#ViewModelKey(ActMainViewModel::class)
abstract fun mainActivityViewModel(viewModel: ActMainViewModel): ViewModel
}
FragmentBuilder:
#Module
abstract class FragmentBuilder {
#FragmentScope
#ContributesAndroidInjector
abstract fun homeFragment(): HomeFragment?
}
ActivityBuilder :
#Module
abstract class ActivityBuilder {
#ActivityScope
#ContributesAndroidInjector
abstract fun loginActivity(): ActivityLogin?
#ActivityScope
#ContributesAndroidInjector
abstract fun mainActivity(): MainActivity?
}
AppModule :
#Module
abstract class AppModule {
#Binds
#Singleton
abstract fun application(app: App?): Application?
}
ActivityScope :
#Scope
#Retention(value = AnnotationRetention.RUNTIME)
annotation class ActivityScope
FragmentScope :
#Scope
#Retention(value = AnnotationRetention.RUNTIME)
annotation class FragmentScope {
}
AppComponent :
#Singleton
#ApplicationScope
#Component(
modules = [AppModule::class, AndroidSupportInjectionModule::class, RemoteModule::class, ActivityBuilder::class,
FragmentBuilder::class, ViewModelModule::class]
)
interface AppComponent : AndroidInjector<App> {
fun inject(membersFragment: MembersFragment)
override fun inject(myApplication: App)
fun inject(instance: DaggerApplication)
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: App): Builder
fun build(): AppComponent
}
}
Application :
class App : DaggerApplication() {
var appComponent: AppComponent? = null
override fun applicationInjector(): AppComponent {
appComponent = DaggerAppComponent.builder().application(this).build()
appComponent!!.inject(this)
return appComponent!!
}
}
and used in ViewModel :
class LoanViewModel #Inject constructor() : ViewModel() {
#Inject
lateinit var apiService: ApiService
}
I've just refactored my dagger code to make it scalable and move all core stuff to a separate module called di.
Now when I try to inject my dependencies in the app module I got this :
[Dagger/MissingBinding] retrofit2.Retrofit cannot be provided without an #Inject constructor or an #Provides-annotated method.
public abstract interface ResetPasswordComponent {
^
retrofit2.Retrofit is injected at
com.sahra.oms.ibshop.features.resetpassword.di.ResetPasswordNetworkModule.providerResetPasswordAPI(retrofit)
com.sahra.oms.ibshop.data.remote.service.ResetPasswordService is injected at
com.sahra.oms.ibshop.data.repisotory.nationalid.UniqueRepositoryImpl(resetPasswordService)
com.sahra.oms.ibshop.data.repisotory.nationalid.UniqueRepositoryImpl is injected at
com.sahra.oms.ibshop.features.resetpassword.di.ResetPasswordModule.bindUniqueIdRepository(uniqueRepositoryImpl)
com.sahra.oms.ibshop.data.repisotory.nationalid.UniqueIdRepository is injected at
com.sahra.oms.ibshop.features.resetpassword.uniqueid.CheckUniqueIDViewModel(repository)
com.sahra.oms.ibshop.features.resetpassword.uniqueid.CheckUniqueIDViewModel is injected at
com.sahra.oms.ibshop.features.resetpassword.di.ResetPasswordModule.bindCheckIdViewModel(checkUniqueIDViewModel)
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
com.sahra.oms.ibishop.di.util.ViewModelFactory(viewModelsMap)
com.sahra.oms.ibishop.di.util.ViewModelFactory is injected at
com.sahra.oms.ibshop.di.ViewModelBuilder.bindViewModelFactory(arg0)
androidx.lifecycle.ViewModelProvider.Factory is injected at
com.sahra.oms.ibshop.features.resetpassword.newpassword.NewPasswordFragment.viewModelFactory
com.sahra.oms.ibshop.features.resetpassword.newpassword.NewPasswordFragment is injected at
com.sahra.oms.ibshop.features.resetpassword.di.ResetPasswordComponent.inject(com.sahra.oms.ibshop.features.resetpassword.newpassword.NewPasswordFragment)
ResetPasswordService is just a Retrofit interface.
Here is my code:
AppComponent
#Singleton
#AppScope
#Component
interface AppComponent {
fun provideContextComponent(): ContextComponent
fun provideNetworkComponent(): NetworkComponent
fun provideSharedPrefComponent(): SharedPreferencesComponent
fun inject(app: Application)
#Component.Factory
interface Factory {
fun create(
#BindsInstance
context: ContextComponent,
#BindsInstance
network: NetworkComponent,
#BindsInstance
sharedPrefs: SharedPreferencesComponent
): AppComponent
}
}
NetworkComponent :
#Scope
#Retention(AnnotationRetention.RUNTIME)
annotation class NetworkScope
#NetworkScope
#Component(
dependencies = [ContextComponent::class],
modules = [OkHttpModule::class, AuthBinderModule::class]
)
interface NetworkComponent {
fun provideOkHttp(): OkHttpClient
fun provideRetrofit(): Retrofit
fun provideGson(): GsonConverterFactory
}
OkHttpModule :
#Module
object OkHttpModule {
private const val BASE_URL = "base_url"
#Provides
#JvmStatic
fun provideLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor(
HttpLoggingInterceptor.Logger { message -> Log.d("<<<network>>>", message) }).apply {
level = if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY else HttpLoggingInterceptor.Level.NONE
}
}
#Provides
#JvmStatic
fun provideChuckInterceptor(app: Application): ChuckInterceptor = ChuckInterceptor(app)
#Provides
#JvmStatic
fun provideOkhttpCache(app: Application): Cache =
Cache(app.cacheDir, 50_000_000)
#Provides
#NetworkScope
#JvmStatic
fun provideClient(
loggingInterceptor: HttpLoggingInterceptor,
chuckInterceptor: ChuckInterceptor,
authInterceptor: Interceptor,
cache: Cache
): OkHttpClient {
return OkHttpClient.Builder()
.cache(cache)
.addInterceptor(loggingInterceptor)
.addInterceptor(authInterceptor)
.addInterceptor(chuckInterceptor)
.build()
}
#Provides
#NetworkScope
#JvmStatic
fun provideGson() = Gson()
#Provides
#JvmStatic
fun provideGsonConverter(gson: Gson) = GsonConverterFactory.create(gson)
#Provides
#NetworkScope
#JvmStatic
fun provideRetrofit(
gsonConverterFactory: GsonConverterFactory,
client: Lazy<OkHttpClient>
): Retrofit = Retrofit.Builder()
.callFactory { request -> client.get().newCall(request) }
.baseUrl(BASE_URL)
.addConverterFactory(gsonConverterFactory)
.build()
}
Here is how I try to inject the dependencies:
#FeatureScope
#Component(
dependencies = [AppComponent::class],
modules = [
ResetPasswordNetworkModule::class,
ResetPasswordModule::class,
ViewModelBuilder::class
]
)
interface ResetPasswordComponent {
fun inject(newPasswordFragment: NewPasswordFragment)
fun inject(checkUniqueIDFragment: CheckUniqueIDFragment)
#Component.Builder
interface Builder {
fun coreComponent(appComponent: AppComponent): Builder
fun build(): ResetPasswordComponent
}
}
#Module
abstract class ResetPasswordModule {
#Binds
abstract fun bindResetPasswordRepository(resetPasswordRepositoryImpl: ResetPasswordRepositoryImpl): ResetPasswordRepository
#Binds
abstract fun bindUniqueIdRepository(uniqueRepositoryImpl: UniqueRepositoryImpl): UniqueIdRepository
#Binds
#IntoMap
#ViewModelKey(CheckUniqueIDViewModel::class)
abstract fun bindCheckIdViewModel(checkUniqueIDViewModel: CheckUniqueIDViewModel): ViewModel
#Binds
#IntoMap
#ViewModelKey(NewPasswordViewModel::class)
abstract fun bindNewPasswordViewModel(newPasswordViewModel: NewPasswordViewModel): ViewModel
}
#Module
object ResetPasswordNetworkModule {
#Provides
#JvmStatic
#FeatureScope
fun provideUserAPI(
retrofit: Retrofit
): ResetPasswordService = retrofit.create(ResetPasswordService::class.java)
}
and here is my repository code:
class ResetPasswordRepositoryImpl #Inject constructor(
private val resetPasswordService: ResetPasswordService
) : ResetPasswordRepository {
}
Fragment :
class NewPasswordFragment{
#Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
}
class NewPasswordViewModel #Inject constructor(
private val repository: ResetPasswordRepository
)
Thanks in advance.
Your app component factory looks like this:
fun create(
#BindsInstance
context: ContextComponent,
#BindsInstance
network: NetworkComponent,
#BindsInstance
sharedPrefs: SharedPreferencesComponent
): AppComponent
This provides access to an instance of NetworkComponent, so any #Provides, #Binds, or #Inject that requires a NetworkComponent can get one. It does not, however, give direct access to NetworkComponent's object graph.
NetworkComponent already exposes Retrofit, so you can certainly get one if you have access to the component. However, this process is not automatic, and in your setting requires a #Provides method.
#Provides
fun provideRetrofit(component: NetworkComponent): Retrofit = component.provideRetrofit()
This is more convoluted that it needs to be. A better way to accomplish this is to make NetworkComponent a dependency of AppComponent (or just use its modules and drop the network component entirely), then expose Retrofit in AppComponent.
// using multiple scoped dependencies requires Dagger 2.27
#AppScope
#Component(dependencies = [ContextComponent::class, NetworkComponent::class, SharedPreferencesComponent::class])
interface AppComponent {
fun provideRetrofit(): Retrofit
// ...
}
I am newbie in dependency injection with Dagger in Android
I have this error Unable to start activity ComponentInfo{MainActivity}: kotlin.UninitializedPropertyAccessException: lateinit property dispatchingAndroidInjector has not been initialized
I can't know what is the exact error in my code, Should I change something ? Although in another project I work on, they have the same code except AndroidInjection.inject(this) and the code is working perfectly
Thanks a lot for your help
Here is my files:
AppComponent
#Singleton
#Component(
modules = [AndroidSupportInjectionModule::class,
AppModule::class, NetworkModule::class,
ActivityBuilder::class]
)
interface AppComponent : AndroidInjector<Application> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
}
AppModule
#Module(includes = [ViewModelModule::class])
class AppModule {
#Provides
fun provideContext(application: MyApplication): Context {
return application.applicationContext
}
#Provides
fun provideHandler(): Handler {
return Handler()
}
#Provides
#Singleton
fun provideSharedPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences("prefs", Context.MODE_PRIVATE)
}
#Provides
#Singleton
fun provideSharedPreferenceUtils(context: Context): SharedPreferenceUtils {
return SharedPreferenceUtils(provideSharedPreferences(context))
}
}
ActivityBuilder
#Module
abstract class ActivityBuilder {
#ContributesAndroidInjector
abstract fun bindMainActivity(): MainActivity
}
NetworkModule
#Module(includes = [AppModule::class])
// Safe here as we are dealing with a Dagger 2 module
#Suppress("unused")
class NetworkModule {
#Provides
#Singleton
fun provideCache(context: Context): Cache {
return Cache(context.cacheDir, CACHE_SIZE)
}
#Provides
#Singleton
fun provideOkHttpClient(cache: Cache, context: Context): OkHttpClient {
return OkHttpClient.Builder()
.cache(cache)
.addInterceptor { chain ->
var request = chain.request()
request = if (hasNetwork(context)!!)
request.newBuilder().header("Cache-Control", "public, max-age=" + 5).build()
else
request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7).build()
chain.proceed(request)
}
.build()
}
/**
* Provides the Quotes service implementation.
* #param retrofit the Retrofit object used to instantiate the service
* #return the Quote service implementation.
*/
#Provides
#Singleton
fun provideQuotesAPI(retrofit: Retrofit): QuotesAPI {
return retrofit.create(QuotesAPI::class.java)
}
/**
* Provides the Retrofit object.
* #return the Retrofit object
*/
#Provides
#Singleton
fun provideRetrofitInterface(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(MoshiConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.client(okHttpClient)
.build()
}
}
ViewModelModule
#Module
abstract class ViewModelModule {
#Binds
abstract fun bindViewModelFactory(factory: MyViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(QuotesListViewModel::class)
internal abstract fun bindsQuotesListViewModel(quotesListViewModel: QuotesListViewModel): ViewModel
#Binds
#IntoMap
#ViewModelKey(QuoteListItemViewModel::class)
internal abstract fun bindsQuoteListItemViewModel(quoteListItemViewModel: QuoteListItemViewModel): ViewModel
}
MyApplication
class MyApplication : Application(), HasActivityInjector {
#Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Activity>
override fun activityInjector(): AndroidInjector<Activity> = dispatchingAndroidInjector
override fun onCreate() {
super.onCreate()
// StateSaver.setEnabledForAllActivitiesAndSupportFragments(this, true)
// initialise dependency injection
DaggerAppComponent
.builder()
.application(this)
.build()
.inject(this)
}
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
MultiDex.install(this)
}
}
MainActivity
class MainActivity : DaggerAppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
private lateinit var binding: ActivityMainBinding
private lateinit var menu: Menu
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
}
}
Finally, after several attempts, I found my error, in AppComponent
I should call MyApplication class and not Application
#Singleton
#Component(
modules = [AndroidSupportInjectionModule::class,
AppModule::class, NetworkModule::class,
ActivityBuilder::class]
)
interface AppComponent : AndroidInjector<MyApplication> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: MyApplication): Builder
fun build(): AppComponent
}
There have been many other similar questions, but none of the answers have been applicable to my code. I cannot figure out what I have done wrong.
First I have a NetworkModule that is used as a module for the ApplicationComponent:
#Module
open class NetworkModule {
companion object {
private val BASE = "http://www.example.com/"
}
#Provides #ApplicationScope
fun provideClient(): OkHttpClient = OkHttpClient()
#Provides #ApplicationScope
fun provideMoshi(): Moshi {
return Moshi.Builder().add(InstantAdapter).add(UriAdapter).build()
}
#Provides #ApplicationScope
fun provideRetrofit(client: OkHttpClient, moshi: Moshi): Retrofit {
return Retrofit.Builder().client(client).baseUrl(BASE)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
}
#Provides #ApplicationScope
fun provideArticleService(retrofit: Retrofit): ArticleService {
return retrofit.create(ArticleService::class.java)
}
}
#ApplicationScope #Component(modules = arrayOf(ContextModule::class, RealmModule::class, NetworkModule::class))
interface ApplicationComponent {}
Then the ApplicationComponent is built in my Application class:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
AndroidThreeTen.init(this)
plantLog()
drawDagger()
}
private fun drawDagger() {
Injector.initializeApplicationComponent(this)
}
// ...
}
object Injector {
lateinit var applicationComponent: ApplicationComponent
private set
fun initializeApplicationComponent(context: Context) {
applicationComponent = DaggerApplicationComponent.builder()
.contextModule(ContextModule(context))
.networkModule(NetworkModule())
.realmModule(RealmModule())
.build()
}
// ...
}
Then I have an ActivityModule that is used in the ActivityComponent (which has ApplicationComponent as a dependency):
#Module
open class ActivityModule(private val activity: AppCompatActivity) {
#Provides #ActivityScope #ActivityContext
fun provideContext(): Context = activity
#Provides #ActivityScope
fun provideFragmentManager(): FragmentManager = activity.supportFragmentManager
}
#ActivityScope #Component(dependencies = arrayOf(ApplicationComponent::class), modules = arrayOf(ActivityModule::class))
interface ActivityComponent {
fun inject(activity: MainActivity)
}
Finally, I create a new ActivityComponent in the MainActivity and #Inject the ArticleService:
class MainActivity : AppCompatActivity() {
#Inject lateinit var service: ArticleService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
DaggerActivityComponent.builder()
.applicationComponent(Injector.applicationComponent)
.activityModule(ActivityModule(this))
.build().inject(this)
service.getNewsArticles()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.subscribe(
{ response -> onNext(response) },
{ error -> onError(error) })
}
// ...
}
But when I try to build I get the following error, even though I believe the provideArticleService() function in NetworkModule is annotated correctly:
ArticleService cannot be provided without an #Provides- or
#Produces-annotated method.
You're missing the provision methods to inherit to your Activity scoped component. Either use subcomponents instead of component dependency, or define the provision methods in your application component.
#ApplicationScope #Component(modules = arrayOf(ContextModule::class, RealmModule::class, NetworkModule::class))
interface ApplicationComponent {
ArticleService articleService();
}