Dagger2 Binding issue - android

I have two modules one for ViewModelModule Providers and another for Application level which contains retrofit,intent. So before passing to the component I have included the ViewModelModule to the ApplicationModule like this
#Module(includes = [ViewModelModule::class])
class ApplicationModule {
And My component interface is Like:
#Singleton
#Component(modules = [ApplicationModule::class, ContextModule::class])
interface AppComponent {
fun inject(activity: LoginActivity)
fun inject(activity: RegisterActivity)
fun inject(activity: SplashActivity)
}
ApplicationModule class
#Module(includes = [ViewModelModule::class])
class ApplicationModule {
#Singleton
#Named("GotoLogin")
#Provides
fun provideSplashIntent(appCompatActivity: AppCompatActivity): Intent {
return Intent(appCompatActivity, LoginActivity::class.java)
}
#Singleton
#Named("GotoDashboard")
#Provides
fun provideLoginIntent(appCompatActivity: AppCompatActivity): Intent {
return Intent(appCompatActivity, DashboardActivity::class.java)
}
#Singleton
#Named("GotoRegister")
#Provides
fun provideRegisterIntent(appCompatActivity: AppCompatActivity): Intent {
return Intent(appCompatActivity, RegisterActivity::class.java)
}
#Singleton
#Provides
fun provideTimer(): Timer {
return Timer()
}
}
ViewModelModule class
#Module
abstract class ViewModelModule {
#Binds
abstract fun bindviewmodelFactory(factory: DaggerViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(LoginViewModel::class)
abstract fun bindLoginViewModel(loginViewModel: LoginViewModel): ViewModel
#Binds
#IntoMap
#ViewModelKey(RegisterViewModel::class)
abstract fun bindRegisterViewModel(registerViewModel: RegisterViewModel): ViewModel
}
Scenario:
Suppose when I trying to use a function from ApplicationModule Class in LoginActivity then the Error is coming.
Usage:
class LoginActivity : AppCompatActivity() {
#Inject
lateinit var factory: ViewModelProvider.Factory
lateinit var loginViewModel: LoginViewModel
lateinit var context: Context
#Named("GotoRegister")
#Inject
lateinit var regiseterIntent: Intent
But when I am trying to use anything from the ApplicationModule class a compile error is coming
Crony\app\build\tmp\kapt3\stubs\debug\com\app\crony\di\AppComponent.java:8: error: [Dagger/MissingBinding] androidx.appcompat.app.AppCompatActivity cannot be provided without an #Inject constructor or an #Provides-annotated method.
public abstract interface AppComponent {
^
androidx.appcompat.app.AppCompatActivity is injected at
com.app.crony.di.ApplicationModule.provideRegisterIntent(appCompatActivity)
#javax.inject.Named("GotoRegister") android.content.Intent is injected at
com.app.crony.LoginActivity.regiseterIntent
Full Source Code:
Github Link
I can feel that I am missing something but not able to sort out the issue.

Replace activity with context. It will work well.

Related

Gradle build keeps failing due to MissingBinding on Dagger Hilt migration

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.

Field injection in AppWidgetProvider in kotlin using dagger 2

I am using dagger2 and kotlin in my project. I have injected activity and viewmodels and now I want to inject appwidgetprovider class for app widgets. I can`t find a way to inject fields in to appwidgetprovider class. Here is my dagger2 implementaion.
this is App Component class
#Singleton
#Component(
modules = [
UserInformationModule::class,
AndroidInjectionModule::class,
AppModule::class,
MainActivityModule::class,
ServiceBuilderModule::class]
)
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(application: BaseClass)
}
This is AppModule class
#Module(includes = [ViewModelModule::class, CoreDataModule::class])
class AppModule {
#Singleton
#Provides
fun provideMyMyAppService(
#MyAppAPI okHttpClient: OkHttpClient,
converterFactory: MoshiConverterFactory
) = provideService(okHttpClient, converterFactory, MyMyAppApi::class.java)
#MyAppAPI
#Provides
fun providePrivateOkHttpClient(
upstreamClient: OkHttpClient
): OkHttpClient {
return upstreamClient.newBuilder().build()
}
#Singleton
#Provides
fun provideRemoteDataSource(myMyAppService: MyMyAppApi) = RemoteDataSource(myMyAppService)
#Singleton
#Provides
fun provideDb(app: Application) = AppDatabase.getInstance(app)
//other code
This is Fragment Builder Module
#Suppress("unused")
#Module
abstract class FragmentBuildersModule {
#ContributesAndroidInjector
abstract fun homeFragment(): HomeFragment
#ContributesAndroidInjector
abstract fun fragHome(): FragHome
//other code
}
this is my Main Activity Module
#Suppress("unused")
#Module
abstract class MainActivityModule {
#ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
abstract fun contributeMainActivity(): HomeActivity
#ContributesAndroidInjector(modules = [FragmentBuildersModule::class])
abstract fun contributeSplashActivity(): SplashActivity
}
This is my ViewModel Module
#Suppress("unused")
#Module
abstract class ViewModelModule {
#Binds
#IntoMap
#ViewModelKey(HomeViewModel::class)
abstract fun bindHomeViewModel(viewModel: HomeViewModel): ViewModel
///other code
}
I tried to inject appwidgetprivider class using
AndroidInjection.inject(this)
as I did in Service. But this method only excepts Activity, Fragment, service, broadcast receiver and contentproviders. Any help please.
I am using dagger 2.23.2 and kotlin 1.3.41
Appwidget provicer can be injected the same way a broadcast receiver is injected.
By looking at your provided code you can do some thing like this.
Create an abstract function
#ContributesAndroidInjector
internal abstract fun contributeWidget(): YourWidgetClass
extend your Baseclass with HasBroadcastReceiverInjector and implement broadcastReceiverInjector
#Inject
lateinit var broadcastReceiverInjector: DispatchingAndroidInjector<BroadcastReceiver>
override fun broadcastReceiverInjector(): AndroidInjector<BroadcastReceiver> {
return broadcastReceiverInjector
}
and fillany inject in the widgetprovider class in onreceive
before super call
AndroidInjection.inject(this, context)

dagger.internal.codegen.ComponentProcessor was unable to process this interface because not all of its dependencies could be resolved

I've been trying to setup dagger in a multimodule project. My setup is the following:
#Singleton
#Component(modules = [AndroidSupportInjectionModule::class, NetworkModule::class, MovieListActivityModule::class, DetailActivityModule::class])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(app: MoviesApplication)
}
#Module
class NetworkModule { .... }
#Module
abstract class MovieListActivityModule {
#ContributesAndroidInjector(modules = [ListFragmentModule::class, ListNetworkModule::class, ListViewModelsModule::class])
abstract fun bindMovieListActivity(): MovieListActivity
}
#Module
abstract class ListFragmentModule {
#ContributesAndroidInjector
abstract fun bindListFragment(): ListFragment
}
#Module
public class ListNetworkModule {
#Provides
SearchService providesSearchService(Retrofit retrofit) {
return retrofit.create(SearchService.class);
}
}
#Module
abstract class ListViewModelsModule {
#Binds
abstract fun bindDaggerViewModelFactory(daggerViewModelFactory: DaggerViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(ListViewModel::class)
abstract fun bindListViewModel(listViewModel: ListViewModel): ViewModel
}
#Module
abstract class DetailActivityModule {
#ContributesAndroidInjector(modules = [DetailViewModelsModule::class, DetailNetworkModule::class, DbModule::class])
abstract fun bindDetailActivity(): DetailActivity
}
#Module
abstract class DetailViewModelsModule {
#Binds
abstract fun bindDaggerViewModelFactory(daggerViewModelFactory: DaggerViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(DetailsViewModel::class)
abstract fun bindDetailViewModel(detailsViewModel: DetailsViewModel): ViewModel
}
#Module
public class DetailNetworkModule {
#Provides
DetailService providesDetailService(Retrofit retrofit) {
return retrofit.create(DetailService.class);
}
}
#Module
class DbModule {
#Provides
fun providesFavouriteMovieDb(application: Application): FavMovieDb = FavMovieDb.getDatabase(application)
}
class DaggerViewModelFactory #Inject constructor(
private val creators: Map<Class<out ViewModel>, #JvmSuppressWildcards Provider<ViewModel>>)
: ViewModelProvider.Factory {.........}
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.RUNTIME)
#MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
class App : Application(), HasActivityInjector {
#Inject
lateinit var dispatchingActivityInjector: DispatchingAndroidInjector<Activity>
override fun onCreate() {
super.onCreate()
DaggerAppComponent.builder()
.application(this)
.build()
.inject(this)
}
override fun activityInjector(): DispatchingAndroidInjector<Activity>? = dispatchingActivityInjector
}
Till this code everything was ok and works perfectly. But problem appears when I add a new FavouriteActivityModule in AppComponent. Like below:
#Module
abstract class FavouriteViewModelModule {
#Binds
abstract fun bindDaggerViewModelFactory(daggerViewModelFactory: DaggerViewModelFactory): ViewModelProvider.Factory
#Binds
#IntoMap
#ViewModelKey(FavouriteViewModel::class)
abstract fun bindFavouriteViewModel(viewModel: FavouriteViewModel): ViewModel
}
#Module
abstract class FavouriteActivityModule {
#ContributesAndroidInjector(modules = [FavouriteViewModelModule::class, DbModule::class])
abstract fun bindFavouriteActivity(): FavoriteActivity
}
Final AppComponent looks like this (added FavouriteActivityModule::class):
#Singleton
#Component(modules = [AndroidSupportInjectionModule::class, NetworkModule::class, MovieListActivityModule::class, DetailActivityModule::class, FavouriteActivityModule::class])
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(app: MoviesApplication)
}
After adding FavouriteActivityModule::class dagger is no more building and complaining like below:
e: /Users/...../di/AppComponent.java:8: 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.
public abstract interface AppComponent {
^
I've not figured out the reason yet and already spent whole day finding out the reason of this error. Any help? TIA

ViewModel cannot be provided without an #Inject constructor or an #Provides-annotated

Question EDITED
I am injecting ViewModelProvider.Factory to BaseActivity like below
open class BaseActivity : DaggerAppCompatActivity() {
#Inject
lateinit var factories: ViewModelProvider.Factory
inline fun <reified T : ViewModel> getViewModel(): T {
return ViewModelProvider(this, factories).get(T::class.java)
}
}
viewModel only works when we inject then like below.
class MainViewModel #Inject constructor( private val alertStore: AlertStore)
: BaseViewModel(){
fun showDialog(){
viewModelScope.launch {
delay(4000)
alertStore.showToast("Alert after 4 seconds.")
}
}
}
Why this #Inject constructor is necessary in my current implementation
class MainActivity : BaseActivity() {
private lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = getViewModel()
viewModel.showDialog()
}
}
App.kt
class App : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().addContext(this).build()
}
}
AppComponent.kt
#Component(
modules = [
AndroidInjectionModule::class,
AppModule::class,
ActivityBuilder::class,
ViewModelInjector::class
]
)
#Singleton
interface AppComponent : AndroidInjector<App> {
#Component.Builder
interface Builder {
fun addContext(#BindsInstance context: Context): Builder
fun build(): AppComponent
}
}
AppModule.kt
#Module
class AppModule {
#Provides
fun provideViewModelFactories(viewModels: Map<Class<out ViewModel>,
#JvmSuppressWildcards Provider<ViewModel>>):
ViewModelProvider.Factory {
return object : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
val factory = viewModels[modelClass]?.get() ?: error(
"No factory provided against ${modelClass.name}"
)
#Suppress("UNCHECKED_CAST")
return factory as T
}
}
}
}
ActivityBuilder.kt
#Module
abstract class ActivityBuilder {
//#Scope("")
#ContributesAndroidInjector ///(modules = {MainModelFactory.class})
public abstract MainActivity bindMainActivity();
}
ViewModelInjector.kt
#Module
public abstract class ViewModelInjector {
#Binds
#IntoMap
#ViewModelKey(MainViewModel.class)
public abstract ViewModel providesMainViewModel(MainViewModel model);
}
ViewModelKey.kt
#MapKey
#Retention(AnnotationRetention.SOURCE)
annotation class ViewModelKey(
val value: KClass<out ViewModel>
)
Why do I have to append #Inject constructor to each ViewModel and kindly explain a little why we need #Binds #IntoMap and with ViewModel
When you use dagger android you should make your activities and fragments as extensions of DaggerActivity (and DaggerFragment for fragments).
class MainActivity : DaggerActivity() {
#Inject
lateinit var viewModel: MainViewModel
}
Next you should prepare infrastructure for injection:
Create injectors for each your activity:
// All your injectors can be defined in this module
#Module(includes = [AndroidInjectionModule::class])
interface AppInjectorModule {
#ContributesAndroidInjector(modules = [MainActivityVmModule::class, /*other dependecies*/])
fun getMainActivityInjector(): MainActivity
}
Create modules to provide view models (can be multiple for one activity) and factory
#Module(includes = [VmFactoryModule::class])
abstract class MainActivityVmModule {
// bind implementation of ViewModel into map for ViewModelFactory
#Binds
#IntoMap
#ClassKey(MainViewModelImpl::class)
abstract fun bindMainVm(impl: MainViewModelImpl): ViewModel
#Module
companion object {
#Provides
#JvmStatic
fun getMainVm(activity: MainActivity, factory: ViewModelProvider.Factory): MainViewModel {
// create MainViewModelImpl in scope of MainActivity and inject dependecies by ViewModelFactory
return ViewModelProviders.of(activity, factory)[MainViewModelImpl::class.java]
}
}
}
Factory can be provided by different module to avoid duplication
#Module
interface VmFactoryModule {
#Binds
// bind your implementation of factory
fun bindVmFactory(impl: ViewModelFactory): ViewModelProvider.Factory
}
Add activities injectors to AppComponent graph
#Component(
modules = [
AppInjectorModule::class
]
)
#Singleton
interface AppComponent : AndroidInjector<App>
Additional info: Dagger & Android

How to #Inject an abstract BaseActivity and its subclasses using dagger.android?

I'm using the new dagger.android package from Dagger 2 to inject Android dependencies in my project.
I need all my Activities to be a subclass of an abstract BaseActivity
In my BaseActivity I have member variables to be injected. This way:
abstract class BaseActivity : AppCompatActivity() {
#Inject
lateinit var prefs: MyPreferenceDataStore
...// more #Injected members
}
I do it because I want subclasses of BaseActiviy can have access to injected members of BaseActivity:
class SubClassActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle) {
val x = prefs.getXXX //use prefs variable from parent class
}
}
This is my ApplicationComponent:
#Singleton #Component(modules = arrayOf(
ApplicationModule::class,
ActivityBindingModule::class,
AndroidSupportInjectionModule::class
))
interface ApplicationComponent {
#Component.Builder interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): ApplicationComponent
}
fun inject(app: AndroidApplication)
}
The ApplicationModule class has simple #Provides annotated methods:
#Module
class ApplicationModule {
#Singleton #Provides
fun providesMyPreferenceDataStore(context: Context): MyPreferenceDataStore {
return MyPreferenceDataStoreImpl(context)
}
// more #Provides annotated methods
}
I think the problem is in my ActivityBindingModule
#Module
abstract class ActivityBindingModule {
#PerActivity
#ContributesAndroidInjector(
modules = arrayOf(BaseActivityModule::class
))
abstract fun bindBaseActivity(): BaseActivity
#PerActivity
#ContributesAndroidInjector(
modules = arrayOf(
BaseActivityModule::class
))
abstract fun bindSubClassActivity(): SubClassActivity
}
This is what I have tried so far:
Make the bindSubClassActivity() method not to depend of BaseActivityModule::class, didn't work.
Move the providesMyPreferenceDataStore from ApplicationModule to the BaseActivityModule, so that the class is:
#Module
class BaseActivityModule {
#PerActivity #Provides
fun providesMyPreferenceDataStore(context: Context): MyPreferenceDataStore {
return MyPreferenceDataStoreImpl(context)
}
}
And this is the error I'm getting:
Error: [dagger.android.AndroidInjector.inject(T)] com.example.BaseActivity cannot
be provided without an #Provides-annotated method.
This type supports members injection but cannot
be implicitly provided.
I didn't understand exactly what you try to do but this solutions based in what i understand
AppComponent should look like this
#Singleton
#Component(modules = [
AndroidSupportInjectionModule::class,
AppModule::class,
ActivityModule::class])
interface AppComponent : AndroidInjector<DaggerApplication> {
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
}
your base activity which will inject all the objects
abstract class BaseActivity : DaggerAppCompatActivity() {
#Inject
lateinit var prefs: SharedPreferences
//other objects to inject
}
The activity that will inherit from it eg:MainActivity
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
prefs.getBoolean("s", true)
}
}
And Activity module
#Module
abstract class ActivityModule {
#ContributesAndroidInjector
abstract fun bindMainActivity(): MainActivity
#ContributesAndroidInjector
abstract fun bindBaseActivity():BaseActivity
}
AppModule
#Module
class AppModule {
#Singleton
#Provides
fun providesMyPreferenceDataStore(application: Application): SharedPreferences {
return application.getSharedPreferences("test", Context.MODE_PRIVATE)
}
}

Categories

Resources