I’ve been working on upgrading our Android project from Android Studio 3.0.1 to 4.0.1, and as part of that, I have upgraded the following:
Build Tools Version 26.0.2 -> 30.0.2
Compile SDK Version 25 -> 28
Kotlin version 1.2.71 -> 1.4.10
Gradle version 3.1.0 -> 4.0.1
Android Support v7 -> androidx 1.0.0
Dagger 2.9 -> 2.28.3
Retrofit 2.2.0 -> 2.9.0
Okhttp 3.7.0 -> 4.9.0
I am now running into an issue where I am getting an error saying the following:
#dagger.Component(dependencies = {org.bbb.ccc.app.application.AppComponent.class}, modules = {org.bbb.ccc.app.ddd.yyy.zzz.class})
^
#Singleton org.bbb.ccc.app.application.AppComponent/Users/xxx/aaa_android/app/build/tmp/kapt3/stubs/eee/org/xxx/ddd/app/application/AppComponent.java:8: error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends error.NonExistentClass>,javax.inject.Provider<org.bbb.ccc.app.dagger.fragment.FragmentComponentBuilder<?,?>>> cannot be provided without an #Provides-annotated method.
So far, as part of this migration, I have moved several classes from being a Singleton to their own scope, as discussed in Singleton component cannot depend on scoped components
I have also tried to follow the information here: http://frogermcs.github.io/activities-multibinding-in-dagger-2/ as well as a few other things, and I have not yet been able to get the build to compile with AS4. This project compiled in 3.0.1 before upgrading.
Onto code now:
App.kt:
package org.bbb.ccc.app.application
import android.app.Activity
import android.app.Application
import android.content.Context
import android.content.Intent
import android.os.StrictMode
import android.util.Log
import com.crashlytics.android.Crashlytics
import io.fabric.sdk.android.Fabric
import io.realm.Realm
import io.realm.RealmConfiguration
import org.bbb.ccc.app.dagger.activity.ActivityComponentBuilder
import org.bbb.ccc.app.dagger.activity.HasActivitySubcomponentBuilders
import org.bbb.ccc.app.data.migration.KäsefüßeRealmMigration
import org.bbb.ccc.app.data.repository.RealmDatabase
import org.bbb.ccc.app.utils.logouttimer.LogoutTimerService
import javax.inject.Inject
/**
* Application Class (Debug Version)
* #desc - Instantiate a Base class for all Android Activities and Services. Also creates global
* objects for Realm, Dagger, Analytics, Memory Management, etc.
*/
open class App : Application(), HasActivitySubcomponentBuilders {
companion object {
var context: Context? = null
}
#Inject
lateinit var activityComponentBuilders: MutableMap<Class<out Activity?>?, #JvmSuppressWildcards ActivityComponentBuilder<*, *>?>
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
context = this
// Set Crashlytics for Firebase
Fabric.with(this, Crashlytics())
createRealm("KäsefüßeDebug.Realm", 4L)
appComponent = createAppComponent()
appComponent.inject(this)
try{
Intent(this, LogoutTimerService::class.java).also { intent ->
startService(intent)
}
} catch (e: IllegalStateException){
Log.d("Käsefüße", e.toString())
}
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build())
StrictMode.setVmPolicy(StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build())
}
override fun onTerminate() {
LogoutTimerService.sharedInstance.cancelTimer()
Intent(this, LogoutTimerService::class.java).also { intent ->
stopService(intent)
}
super.onTerminate()
}
protected open fun createAppComponent() : AppComponent {
return DaggerAppComponent.builder()
.appModule(AppModule(this))
.apiModule(ApiModule())
.dbModule(DbModule())
.build()
}
protected open fun createRealm(realmName: String, realmVersion: Long) {
Realm.init(this)
RealmDatabase.realmConfiguration = RealmConfiguration.Builder()
.name(realmName)
.schemaVersion(realmVersion)
.migration(KäsefüßeRealmMigration())
.build()
}
override fun getBuilder(activityClass: Class<out Activity?>?): ActivityComponentBuilder<*, *>? {
return activityComponentBuilders[activityClass]
}
}
AppComponent.kt:
package org.bbb.ccc.app.application
import android.content.Context
import dagger.Component
import org.bbb.ccc.app.vol.ComplimenteuseService
import org.bbb.ccc.app.dagger.activity.ActivityBindingModule
import org.bbb.ccc.app.data.preference.PreferenceFactory
import org.bbb.ccc.app.Fromage.FromageService
import org.bbb.ccc.app.nourriture.Ananas.AnanasActivity
import org.bbb.ccc.app.nourriture.pommeDuTerre.PommeDuTerreActivity
import javax.inject.Singleton
#Singleton
#Component(modules = [AppModule::class, ActivityBindingModule::class])
interface AppComponent {
fun inject(app: App)
fun inject(FromageService: FromageService)
fun inject(complimenteuseService: ComplimenteuseService)
fun context() : Context
fun preferenceFactory() : PreferenceFactory
fun inject(ananasActivity: AnanasActivity)
fun inject(pommeDuTerreActivity: PommeDuTerreActivity)
}
AppModule.kt:
package org.bbb.ccc.app.application
import android.content.Context
import android.content.SharedPreferences
import com.google.gson.Gson
import dagger.Module
import dagger.Provides
import io.reactivex.Scheduler
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import org.bbb.ccc.app.bluetooth.BTUtils
import org.bbb.ccc.app.data.preference.PreferenceFactory
import org.bbb.ccc.app.domain.model.Profile
import org.bbb.ccc.app.domain.network.KäsefüßeAPI
import org.bbb.ccc.app.domain.repository.ComplimenteuseRepository
import org.bbb.ccc.app.domain.repository.TrainRepository
import org.bbb.ccc.app.domain.repository.UserRepository
import org.bbb.ccc.app.login.interactor.ProfileInteractor
import org.bbb.ccc.app.login.interactor.ProfileManager
import org.bbb.ccc.app.presentation.interactor.SchedulerProvider
import org.bbb.ccc.app.utils.android.DeviceNameGenerator
import org.bbb.ccc.app.utils.android.HasConnection
import org.bbb.ccc.app.utils.connectionstate.ConnectionBroadCastReceiverRegistry
import org.bbb.ccc.app.utils.connectionstate.ConnectionNotifier
import org.bbb.ccc.app.utils.connectionstate.OkHttpPingService
import org.bbb.ccc.app.utils.extensions.hasInternetConnection
import javax.inject.Singleton
/**
* App Module - Dagger.
*/
#Module(includes = [ApiModule::class, DbModule::class])
class AppModule(private val application: App) {
#Provides
#Singleton
fun app(): App = application
#Provides
#Singleton
fun sharedPreferences(): SharedPreferences =
application.getSharedPreferences("PREFS", Context.MODE_PRIVATE)
#Provides
#Singleton
fun applicationContext() : Context = application
#Provides
#Singleton
fun schedulerProvider() : SchedulerProvider {
return object : SchedulerProvider {
override fun ui(): Scheduler = AndroidSchedulers.mainThread()
override fun computation(): Scheduler = Schedulers.computation()
override fun trampoline(): Scheduler = Schedulers.trampoline()
override fun newThread(): Scheduler = Schedulers.newThread()
override fun io(): Scheduler = Schedulers.io()
}
}
#Provides
#Singleton
fun preferencesManager(sharedPreferences: SharedPreferences, gson: Gson) : PreferenceFactory =
PreferenceFactory(sharedPreferences, gson)
#Provides
#Singleton
fun profileInteractor(preferenceFactory: PreferenceFactory, trainRepository: TrainRepository,
userRepository: UserRepository, schedulerProvider: SchedulerProvider)
: ProfileManager {
val preference = preferenceFactory.create<Profile>(PreferenceFactory.PreferenceKey.PROFILE)
return ProfileInteractor(preference, trainRepository, userRepository, schedulerProvider)
}
#Provides
#Singleton
fun hasConnection() : HasConnection {
return object : HasConnection {
override fun available(): Boolean = application.hasInternetConnection()
}
}
#Provides
#Singleton
fun connectionNotifier(context: Context, hasConnection: HasConnection, schedulerProvider: SchedulerProvider)
: ConnectionNotifier =
ConnectionNotifier(OkHttpPingService(), ConnectionBroadCastReceiverRegistry(context), hasConnection, schedulerProvider)
#Provides
#Singleton
fun ComplimenteuseInteractor(ComplimenteuseRepository: ComplimenteuseRepository,
KäsefüßeAPI: KäsefüßeAPI,
hasConnection: HasConnection,
preferenceFactory: PreferenceFactory,
schedulerProvider: SchedulerProvider): ComplimenteuseManager {
val preference = preferenceFactory.create<Profile>(PreferenceFactory.PreferenceKey.PROFILE)
val deviceNameGenerator = object : DeviceNameGenerator {
override fun name(): String = BTUtils.deviceName
}
return ComplimenteuseInteractor(ComplimenteuseRepository, KäsefüßeAPI, hasConnection, preference, deviceNameGenerator, schedulerProvider)
}
}
ActivityBindingModule:
package org.bbb.ccc.app.dagger.activity
import dagger.Binds
import dagger.Module
import dagger.multibindings.IntoMap
import org.bbb.ccc.app.vol.AeroplanActivity
import org.bbb.ccc.app.vol.AeroplanComponent
import org.bbb.ccc.app.login.LoginActivity
import org.bbb.ccc.app.login.LoginComponent
import org.bbb.ccc.app.nourriture.viande.BoeufActivity
import org.bbb.ccc.app.nourriture.viande.BoeufComponent
import org.bbb.ccc.app.nourriture.fruit.PommeComponent
import org.bbb.ccc.app.nourriture.fruit.CarambolaActivity
import org.bbb.ccc.app.nourriture.fruit.RaisinActivity
import org.bbb.ccc.app.nourriture.fruit.RaisinComponent
import org.bbb.ccc.app.KäsefüßeKäse.KäseActivity
import org.bbb.ccc.app.KäsefüßeKäse.KäseComponent
/**
* Created by Bob Jones on 4/26/17.
*/
#Module(
subcomponents = [LoginComponent::class, AeroplanComponent::class, KäseComponent::class, PommeComponent::class, BoeufComponent::class, RaisinComponent::class]
)
abstract class ActivityBindingModule {
#Binds
#IntoMap
#ActivityKey(LoginActivity::class)
abstract fun loginComponentBuilder(impl: LoginComponent.Builder) : ActivityComponentBuilder<*, *>
#Binds
#IntoMap
#ActivityKey(AeroplanActivity::class)
abstract fun AeroplanComponentBuilder(impl: AeroplanComponent.Builder) : ActivityComponentBuilder<*, *>
#Binds
#IntoMap
#ActivityKey(KäseActivity::class)
abstract fun KäseActivityComponentBuilder(impl: KäseComponent.Builder) : ActivityComponentBuilder<*, *>
#Binds
#IntoMap
#ActivityKey(CarambolaActivity::class)
abstract fun carambolaComponentBuilder(impl: PommeComponent.Builder) : ActivityComponentBuilder<*, *>
#Binds
#IntoMap
#ActivityKey(BoeufActivity::class)
abstract fun boeufActivity(impl: BoeufComponent.Builder) : ActivityComponentBuilder<*, *>
#Binds
#IntoMap
#ActivityKey(RaisinActivity::class)
abstract fun RaisinComponentBuilder(impl: RaisinComponent.Builder) : ActivityComponentBuilder<*, *>
}
Thanks in advance for your assistance!
It says the FragmentComponentBuilder is missing code - I don't see where you create the FragmentComponentBuilder in your post so maybe start looking there and checking that all your fragments are being properly injected.
I am new to dagger 2. I was making a CarComponent on kotlin, I was trying to call my DaggerCarComponent with horsePower value without calling petrolEngineModule. the following is my code :
import dagger.BindsInstance
import dagger.Component
import javax.inject.Singleton
#Component (
modules = [WheelModule::class, PetrolEngineModule::class]
)
interface CarComponent {
fun getCar(): Car
fun inject(mainActivity: MainActivity)
#Component.Builder
interface Builder {
#BindsInstance
fun horsePower(horsePower : Int) : Builder
fun build(): CarComponent
}
}
this is PetrolEngine.kt:
package com.example.daggerapp
import android.util.Log
import javax.inject.Inject
class PetrolEngine : Engine {
private var horsePower : Int
#Inject constructor(horsePower: Int){
this.horsePower = horsePower
}
override fun start() {
Log.d("Engine", "Broom..., horsePower: ${this.horsePower}")
}
}
this is PetrolEngineModule.kt:
package com.example.daggerapp
import dagger.Module
import dagger.Provides
import javax.inject.Inject
#Module
class PetrolEngineModule {
private var horsePower: Int
#Inject constructor(horsePower: Int) {
this.horsePower = horsePower
}
#Provides
fun provideHorsePower(): Int {
return horsePower
}
#Provides
fun provideEngine(engine: PetrolEngine): Engine
{
return engine
}
}
I added the DaggerComponent here as DaggerCarComponent :
package com.example.daggerapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import javax.inject.Inject
class MainActivity : AppCompatActivity() {
#Inject
lateinit var car:Car
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val daggerCar: CarComponent = DaggerCarComponent.builder().petrolEngineModule(PetrolEngineModule(140)).build()
daggerCar.inject(this)
Log.d("Car instance", "$car")
car.drive()
}
}
I was following this tutorial: https://www.youtube.com/watch?v=3tIvekCTSJg&list=PLrnPJCHvNZuA2ioi4soDZKz8euUQnJW65&index=8
In your Builder:
#BindsInstance
Builder horsePower(#Named("horse_power") int horsePower);
After this you will be able to pass horsePower from MainActivity without passing instance of PetrolEngineModule.And same way in ur PetrolEngine constructor:
#Inject
public PetrolEngine(#Named("horse_power") int horsePower) {
this.horsePower = horsePower;
}
And in your PetrolEngineModule u can remove everything and just leave #Provides for PetrolEngine.
Remove #Inject in the module class, cause that what #BindsInstance is doing when pass horsePower it in #Component.Builder
Just an optimization for future viewers. Just replace the existing code with this. Now Dagger will not create the --Provide-- class to provide the instance of PetrolEngine.
Please improve the code if you find anything wrong.
#Module
abstract class PetrolEngineModule {
#Binds
abstract fun bindEngine(engine: PetrolEngine): Engine
}
Hello I am newbie in dagger. I am trying to learn but I am facing a issue in generating AppComponent class. Here is my AppComponent class code. I searched some answer in stackoverflow but none of them not working.
import EmsApplication
import android.app.Application
import com.example.emsapplication.utils.SessionManager
import dagger.BindsInstance
import dagger.Component
import dagger.android.AndroidInjector
import dagger.android.support.AndroidSupportInjectionModule
import javax.inject.Singleton
#Singleton
#Component(modules = \[AndroidSupportInjectionModule::class, ActivityBuildersModule::class, AppModule::class, ViewModelFactoryModule::class\])
interface AppComponent : AndroidInjector<EmsApplication>{
fun sessionManager(): SessionManager?
#Component.Builder
interface Builder {
#BindsInstance
fun application(application: Application?): Builder?
fun build(): AppComponent?
}
}
error: cannot find symbol public abstract interface AppComponent extends dagger.android.AndroidInjector<EmsApplication>
Here is the output of build event.
A silly mistake take my 6 hours to find out hope this answer will help you out.
In my case i didn't give my package in my application class.
Please make sure that You also give your package name in every classes.
try changing your code into.
interface AppComponent : AndroidInjector<EmsApplication> {
fun sessionManager(): SessionManager?
#Component.Builder
interface Builder {
#BindInstance
fun application(application: EmsApplication): Builder
fun build():AppComponent
}
Can you provide more about this ? your EmsApplication. it will help too.
I am trying to set dagger up to provide a data module but I keep running into this error :
error: #Modules cannot be scoped. Did you mean to scope a method instead?
#javax.inject.Singleton()
^
Here is the culprit module :
package com.bottlerocket.dependancyinjection.modules
import android.content.Context
import androidx.room.Room
import com.bottlerocket.dependancyinjection.DI
import com.data.api.BottleRocketApi
import com.data.cache.StoreCache
import com.data.database.CacheDataStoreObject
import com.data.database.RemoteStoreDataObject
import com.data.database.StoreDatabase
import com.data.repository.StoreRepositoryImplementation
import dagger.Module
import dagger.Provides
import interfaces.StoresRepository
import javax.inject.Named
import javax.inject.Singleton
#Module
#Singleton
class DataModule {
#Singleton
#Provides
fun provideRoomDatabase(context: Context): StoreDatabase {
return Room.databaseBuilder(
context,
StoreDatabase::class.java,
"stores_db"
).build()
}
#Provides
#Singleton
fun provideStoreRepository(
api: BottleRocketApi,
#Named(DI.inMemoryCache) cache: StoreCache
): StoresRepository {
val cachedMoviesDataStore = CacheDataStoreObject(cache)
val remoteMoviesDataStore = RemoteStoreDataObject(api)
return StoreRepositoryImplementation(cachedMoviesDataStore, remoteMoviesDataStore)
}
}
As the error indicates, you can't annotate a module with #Singleton, only the functions. So all you have to do is remove the annotation from your module.
Change
#Module
#Singleton
class DataModule {
}
with
#Module
class DataModule {
}
This check was introduced in a recent version of Dagger.
Thanks for reading, After some issues with Annotations after converting a Java App to Kotlin I have added the modules components qualifiers and Scopes and it builds after rebuilding it.But I'm stuck here as I want to use the DaggerFragmentComponent (Fragment Component) but it doesn't import as in it hasn't created that after rebuilding.
I only have access to DaggerCollections which is not want I want to execute the dependency injection....
FragmentModule:
package com.example.daggerappkotlin.di.module
import com.example.daggerappkotlin.di.qualifier.FragContext
import com.example.daggerappkotlin.ui.HomeFragment
import dagger.Module
import dagger.Provides
#Module
class FragmentModule(internal var fragment: HomeFragment) {
#FragContext
#Provides
internal fun provideFragment(): HomeFragment {
return fragment
}
#FragContext
#Provides
internal fun provideConnection(): String {
return "Connected Ben Mohammad"
}
}
FragmentComponent:
package com.example.daggerappkotlin.di.component
import android.content.Context
import com.example.daggerappkotlin.di.module.FragmentModule
import com.example.daggerappkotlin.di.qualifier.ApplicationContext
import com.example.daggerappkotlin.di.scope.FragmentScope
import com.example.daggerappkotlin.ui.HomeFragment
import dagger.Component
#FragmentScope
#Component(dependencies = [ApplicationComponent::class], modules = [FragmentModule::class])
interface FragmentComponent {
#ApplicationContext
val context: Context
fun inject(frag: HomeFragment)
}
ApplicationModule:
package com.example.daggerappkotlin.di.module
import android.content.Context
import com.example.daggerappkotlin.MyApplication
import com.example.daggerappkotlin.di.qualifier.ApplicationContext
import com.example.daggerappkotlin.di.qualifier.DatabaseInfo
import com.example.daggerappkotlin.di.qualifier.NetworkInfo
import dagger.Module
import dagger.Provides
#Module
class ApplicationModule(val application: MyApplication) {
#ApplicationContext
#Provides
internal fun provideContext(): Context {
return application
}
#Provides
#DatabaseInfo
internal fun provideDatabaseName(): String {
return "dummy_db"
}
#Provides
#DatabaseInfo
internal fun provideDatabaseVersion(): Int? {
return 1
}
#Provides
#NetworkInfo
internal fun provideApiKey(): String {
return "SOME_API_KEY"
}
}
ApplicationComponent:
package com.example.daggerappkotlin.di.component
import android.content.Context
import com.example.daggerappkotlin.MyApplication
import com.example.daggerappkotlin.data.local.DatabaseService
import com.example.daggerappkotlin.data.remote.NetworkService
import com.example.daggerappkotlin.di.module.ApplicationModule
import com.example.daggerappkotlin.di.qualifier.ApplicationContext
import dagger.Component
import javax.inject.Singleton
#Singleton
#Component(modules = [ApplicationModule::class])
interface ApplicationComponent {
fun inject(application: MyApplication)
#ApplicationContext
val context: Context
val networkService: NetworkService
val databaseService: DatabaseService
}
I am trying to do the building in MainActivity, HomeFragment and the MyApplication....
GithubRepo