【问题标题】:Inject property into ViewModel using Dagger 2使用 Dagger 2 将属性注入 ViewModel
【发布时间】:2019-01-24 13:36:14
【问题描述】:

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

【问题讨论】:

  • AndroidInjection.inject(this) 应该在super 调用之前完成
  • 知道了。但是如何修复异常?
  • 我不确定它是否适用于 dagger2,除非您将 MainActivityViewModel 添加为依赖关系图的叶子。我会改用构造函数注入(使用自定义 ViewModelProvider 工厂)
  • 能否将 ActivityBuilder 文件中的代码添加到问题中?
  • 正如@Blackbelt 所说,必须将 MainActivityViewModel 添加到依赖关系图中,

标签: android dagger-2


【解决方案1】:

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

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

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

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

@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) 有 dagger 模块,它将提供工厂(从第 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)在您的活动中,当您获取视图模型时,您需要使用来自 dagger 的工厂:(在下面的代码中标记为 // 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 组件的代码,因为我希望这是你已经做过的事情。

您可以阅读有关此内容的更多信息,例如在这个medium article(我不是这篇文章的作者):

【讨论】:

    【解决方案2】:
    @Provides
    fun provideTrips(): List<Trip> {
    

    我对此有点警惕,因为我怀疑 Dagger 的工作是否真的是直接为你提供这个,但我会认为这是理所当然的,暂时忽略它。


    你的代码应该是:

    class MainActivityViewModel @Inject constructor(
        trips: List<Trip>
    ): ViewModel() {
        private val tripsLiveData: MutableLiveData<List<Trip>> = MutableLiveData()
    
        init {
            tripsLiveData.setValue(trips)
        }
    
        fun getTrips() : LiveData<List<Trip>> = tripsLiveData
    }
    

    @Module
    class TripModule{
        @Provides
        // @Singleton // <-- possibly should be here?
        fun provideTrips(): List<Trip> = listOf(
            Trip(100,10),
            Trip(200,20)
        )
    }
    

    @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 viewModelProvider: Provider<MainActivityViewModel>
    
        // this is specifically not marked with `@Inject`
        lateinit var viewModel: MainActivityViewModel
    
        private val 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(){
            viewModel = ViewModelProviders.of(this, object: ViewModelProvider.Factory {
                override fun <T : ViewModel?> create(modelClass: Class<T>): T {
                    if(modelClass == MainActivityViewModel::class.java) {
                        @Suppress("UNCHECKED_CAST")
                        return viewModelProvider.get() as T
                    }
                    throw IllegalArgumentException("Unexpected argument: $modelClass")
                }
            }).get(MainActivityViewModel::class.java)
    
            viewModel.getTrips().observe(this, Observer { 
                val trips = it ?: return@observe
                tripsAdapter.trips = trips 
            })
        }
    }
    

    也就是说,您应该使用构造函数注入,使用Provider&lt;T&gt; 仅在您实际需要时从 Dagger 获取 ViewModel,否则通过 ViewModelProviders.Factory 对其进行初始化,以便您实际从 Dagger 获取 ViewModel。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-08
      • 2017-07-14
      • 2018-12-08
      • 2021-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多