I'm using Dagger2 latest Android Injection model (with AndroidInjection
class) in my application. Until now, everything was working fine, including ViewModelFactory
injection, but I only had parameterless constructors on my ViewModels.
While implementing new features, I've created a ViewModel extending AndroidViewModel
(I need the context in that viewmodel). AndroidViewModel
needs an Application
as constructor parameter, so I've added it as injected parameter in the constructor of my ViewModel.
Since then, Dagger2 does not compile with following error :
AppComponent.java:6: error: [dagger.android.AndroidInjector.inject(T)] android.app.Application cannot be provided without an @Inject constructor or from an @Provides-annotated method.
I understand this means my Application
object is not available in the graph, but I'm adding it in my App class, thus I don't really know how to correct this.
Here are the classes implicated in my process. I'll mark any changes made for my last feature :
class MobileApp : DaggerApplication() {
override fun applicationInjector() = DaggerAppComponent.builder()
.application(this)
.build()
override fun onCreate() {
super.onCreate()
AppInjectorKt.injectApp(this)
}
}
AppComponent.kt :
@Singleton
@Component(modules = arrayOf(
AndroidSupportInjectionModule::class,
AppModule::class,
ActivityBuilder::class))
interface AppComponent : AndroidInjector<MobileApp> {
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: MobileApp): Builder
fun build(): AppComponent
}
override fun inject(app: MobileApp)
}
AppModule.kt :
@Module(includes = arrayOf(ViewModelModule::class))
class AppModule
ActivityBuilder.kt :
@Module
internal abstract class ActivityBuilder {
@ContributesAndroidInjector(modules = arrayOf(ResultModule::class))
internal abstract fun contributeResultActivity(): ResultActivity
//This block is new
@ContributesAndroidInjector(modules = arrayOf(AltimeterActivityModule::class))
internal abstract fun contributeAltimeterActivity(): AltimeterActivity
}
ViewModelModule.kt :
@Module
internal abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(ResultViewModel::class)
abstract fun bindResultViewModel(resultViewModel: ResultViewModel): ViewModel
//This block is new
@Binds
@IntoMap
@ViewModelKey(AltimeterActivityViewModel::class)
abstract fun bindAltimeterActivityViewModel(altimeterActivityViewModel: AltimeterActivityViewModel): ViewModel
@Binds
abstract fun bindViewModelFactory(factory: JumpTrackerViewModelFactory): ViewModelProvider.Factory
}
JumpTrackerViewModelFactory.kt :
@Singleton
class JumpTrackerViewModelFactory @Inject
constructor(private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>)
: ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
var creator: Provider<ViewModel>? = creators[modelClass]
if (creator == null) {
for ((key, value) in creators) {
if (modelClass.isAssignableFrom(key)) {
creator = value
break
}
}
}
if (creator == null) throw IllegalArgumentException("unknown model class " + modelClass)
try {
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
ResultActivity.kt :
class ResultActivity : AppCompatActivity(), Injectable {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private lateinit var viewModel: ResultViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
[...]
viewModel = ViewModelProviders.of(this, viewModelFactory)[ResultViewModel::class.java]
[...]
ResultViewModel.kt :
class ResultViewModel @Inject constructor() : ViewModel() {
[...]
AltimeterActivity.kt (this class is new):
class AltimeterActivity : AppCompatActivity(), Injectable {
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
private lateinit var viewModel: AltimeterActivityViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProviders.of(this, viewModelFactory)[AltimeterActivityViewModel::class.java]
[...]
AltimeterActivityViewModel.kt (This class is new):
class AltimeterActivityViewModel @Inject constructor(app : Application) : AndroidViewModel(app) {
companion object {
val TAG = "AltimeterViewModel"
}
var pressureAltitudeLiveData = PressureAltitudeLiveData(app)
}
Moreover, I've modified my code in order to try App injection, and it's working, so I know app is effectively injected correctly :
AppModule.kt :
@Module(includes = arrayOf(ViewModelModule::class))
class AppModule {
@Singleton @Provides
fun provideThing(app : Application) : String {
return "dqwdwqd"
}
}
AltimeterActivityViewModel.kt :
class AltimeterActivityViewModel @Inject constructor(/*app : Application*/) : /*Android*/ViewModel(/*app*/) {
companion object {
val TAG = "AltimeterViewModel"
}
// var pressureAltitudeLiveData = PressureAltitudeLiveData(app)
}
If anybody has any clue on what's happening, thank you for sharing your thoughts!