使用Dagger 2将属性注入ViewModel [英] Inject property into ViewModel using Dagger 2
问题描述
我尝试学习如何使用Dagger2。请帮助以下异常:
I try to learn how to use Dagger 2. Please help with follow exception:
异常:
UninitializedPropertyAccessException:lateinit物业旅行尚未初始化
Exception: UninitializedPropertyAccessException: lateinit property trips has not been initialized
MainActivityViewModel:
MainActivityViewModel:
class MainActivityViewModel : ViewModel() {
private lateinit var tripsLiveData: MutableLiveData<List<Trip>>
@Inject
lateinit var trips : List<Trip>
fun getTrips() : LiveData<List<Trip>> {
if (!::tripsLiveData.isInitialized){
tripsLiveData = MutableLiveData()
tripsLiveData.value = trips
}
return tripsLiveData
}
}
TripModule:
TripModule:
@Module
class TripModule{
@Provides
fun provideTrips(): List<Trip> {
var list = ArrayList<Trip>()
list.add(Trip(100,10))
list.add(Trip(200,20))
return list
}
}
AppComponent:
AppComponent:
@Singleton
@Component(modules = [
AndroidSupportInjectionModule::class,
ActivityBuilder::class,
TripModule::class])
interface AppComponent{
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(app: MyApplication)
}
MainActivity:
MainActivity:
class MainActivity : AppCompatActivity() {
@Inject
lateinit var tripsAdapter: TripsAdapter
override fun onCreate(savedInstanceState: Bundle?) {
// Inject external dependencies
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupRecyclerView();
setUpViewModel();
}
private fun setupRecyclerView() {
recycler_view.apply {
layoutManager = LinearLayoutManager(context)
adapter = tripsAdapter
}
}
private fun setUpViewModel(){
val model = ViewModelProviders.of(this).get(MainActivityViewModel::class.java)
model.getTrips().observe(this, Observer { tripsAdapter.trips = it!! })
}
}
推荐答案
如果您希望视图模型成为dagger图的一部分,则需要做几件事-使用dagger的多重绑定(只需一次,对于较新的viewmodel来说会更容易) 。您将创建一个新的viewmodel工厂,该工厂将负责实例化viewmodel。该工厂将成为匕首图的一部分,因此将引用通过匕首提供的任何内容。然后,您可以通过 @Inject构造函数(anyParameterFromDagger:Param)
或 @Inject lateinit var someParam:Param
进行构造函数注入
If you want your viewmodel's to be part of the dagger graph, you need to do several things - using dagger's multibindings (just once, for newer viewmodels it will be easier). You'd create new viewmodel factory which will take care of instantiating viewmodels. This factory will be part of dagger graph and therefore will have references to anything provided via dagger. You can then have either constructor injection via @Inject constructor(anyParameterFromDagger: Param)
or @Inject lateinit var someParam: Param
inside the body of viewmodel.
1)为视图模型类创建限定符
1) Create qualifier for view model classes
@MustBeDocumented
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
2)创建从dagger的多重绑定中获取值的viewmodel工厂
2) create viewmodel factory which takes values from dagger's multibindings
@Singleton
class DaggerViewModelFactory @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<out 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)
}
}
}
3)有匕首模块,它将提供工厂(从点2),然后是您的视图模型
3) have dagger module which will provide the factory (from point 2) and then your viewmodels
abstract class YourDaggerModuleWhichThenNeedToBePartOfYourGraphAsIncluded {
@Binds
abstract fun bindViewModelFactory(factory: DaggerViewModelFactory): ViewModelProvider.Factory // this needs to be only one for whole app (therefore marked as `@Singleton`)
@Binds
@IntoMap
@ViewModelKey(MainActivityViewModel::class)
abstract fun bindMainActivityViewModel(vm: MainActivityViewModel): ViewModel // for every viewmodel you have in your app, you need to bind them to dagger
}
4),在获取视图模型时,您需要使用匕首中的工厂:(代码中标记为 // TODO
的位置已更改
4) in your activity, when you get your viewmodel, you need to use the factory from dagger: (places changed marked as // TODO
in the code below)
class MainActivity : AppCompatActivity() {
@Inject
lateinit var tripsAdapter: TripsAdapter
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory // TODO this was added to the activity
override fun onCreate(savedInstanceState: Bundle?) {
// Inject external dependencies
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupRecyclerView();
setUpViewModel();
}
private fun setupRecyclerView() {
recycler_view.apply {
layoutManager = LinearLayoutManager(context)
adapter = tripsAdapter
}
}
private fun setUpViewModel(){
val model = ViewModelProviders.of(this, viewModelFactory)[MainActivityViewModel::class.java] // TODO this was changed
model.getTrips().observe(this, Observer { tripsAdapter.trips = it!! })
}
}
我没有提供将模块添加到dagger组件的代码,因为我希望这是您已经做过的事情。
您可以阅读有关此内容的更多信息,例如在此中型文章中(我不是本文的作者) :
You can read more about this e.g. in this medium article (I'm not author of the article):
这篇关于使用Dagger 2将属性注入ViewModel的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!