使用Dagger 2将属性注入ViewModel [英] Inject property into ViewModel using Dagger 2

查看:129
本文介绍了使用Dagger 2将属性注入ViewModel的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试学习如何使用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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆