Closed mrgaric closed 4 years ago
The injection is done in the activity. So, when the vm init happened, the injection by the activity is not yet done.
Either 1/ you have to inject dependencies in the vm init before using the dependency, either 2/ request the action from the activity, when it is ready.
I prefer the 1/, and even from a general purpose, I prefer handing domain work from vm w/o dependency from view, to respect separation of concern !
@afaucogney is right (thx for the answer!), creation happens before injection. The process is divided into 2 steps:
installViewModelBinding
on your scope
, we create the viewmodel
using the right APIs under the hood.KTP...inject()
inside your activity, it's when we inject the dependencies of the viewmodel
.The issue is that you are trying to access to those dependencies on the first step, when they are not ready yet. This is similar to when you wanna access injected fields inside a constructor. If you are curious, I could explain more about the reasons why we designed it that way.
Nevertheless, there is a way to solve it: using a ViewModel Factory
to create the viewmodel
using Toothpick.
val scope = KTP.openScopes(xxx)
scope.installViewModelBinding<MyViewModel>(activity, InjectedViewModelProvider(scope))
...
// This provider can be reused for any ViewModel
class InjectedViewModelProvider(private val scope: Scope) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return scope.getInstance(modelClass)
}
}
class BackpackViewModel(private val backpack: Backpack) : ViewModel() {
init {
backpack.doSomething()
}
}
By doing so, you can use constructor and field injection within your viewmodel
.
Same, if you are curious, I can provide more info about why we don't provide that functionality out-of-the-box.
@mrgaric If it works for you, plz feel free to close the issue.
thx for the answer. Yes, I am interested, why don't you provide this functionality out of the box?
So, we provide 2 ways of binding view models to your scope: with and without factory. If we use that constructor injection factory by default for the cases when you don't provide one (as you cannot have 2), then the behavior would not be consistent:
As I said, we considered that it would be inconsistent and in order to understand it, you would need to go deep on how TP does VM injection.
Maybe we should provide the Factory within TP anyway so people can use it if they want to? Or at least provide the example on the WIKI? What do you think?
I think an example will be enough. thx.
Hi, I get your sample code for ViewModel
and add an initialization block like here
But I got this exception
java.lang.IllegalStateException: The dependency has not be injected yet.
If i call
backpack.doSomething()
after init call everything is working.How I fix it?