首页 > 解决方案 > 使用 Dagger 2 将属性注入 ViewModel

问题描述

我尝试学习如何使用 Dagger 2。请帮助解决以下异常:

异常:UninitializedPropertyAccessException:lateinit 属性行程尚未初始化

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
    }
}

行程模块:

@Module
class TripModule{
    @Provides
    fun provideTrips(): List<Trip> {

        var list = ArrayList<Trip>()
        list.add(Trip(100,10))
        list.add(Trip(200,20))
        return list
    }
}

应用组件:

@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)
}

主要活动:

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!! })
    }
}

标签: androiddagger-2

解决方案


如果您希望您的视图模型成为匕首图的一部分,您需要做几件事 - 使用匕首的多重绑定(只需一次,对于较新的视图模型会更容易)。您将创建新的视图模型工厂,它将负责实例化视图模型。该工厂将成为 dagger 图的一部分,因此将引用通过 dagger 提供的任何内容。然后,您可以通过视图模型的主体@Inject constructor(anyParameterFromDagger: Param)或内部进行构造函数注入。@Inject lateinit var someParam: Param

1) 为视图模型类创建限定符

@MustBeDocumented
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)

2)创建从匕首的多重绑定中获取值的视图模型工厂

@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点开始),然后是你的视图模型

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 下面的代码)

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 组件的代码,因为我希望这是您已经做过的事情。

您可以在这篇中等文章中阅读更多相关信息(我不是该文章的作者):


推荐阅读