首页 > 解决方案 > 如果没有 @Inject 构造函数或 @Provides-annotated,则无法提供 ViewModel

问题描述

问题已编辑

我正在注入ViewModelProvider.FactoryBaseActivity喜欢下面

open class BaseActivity : DaggerAppCompatActivity() {

    @Inject
    lateinit var factories: ViewModelProvider.Factory

    inline fun <reified T : ViewModel> getViewModel(): T {
        return ViewModelProvider(this, factories).get(T::class.java)
    }
}

viewModel 仅在我们注入时才有效,如下所示。

class MainViewModel @Inject constructor( private val alertStore: AlertStore)
    : BaseViewModel(){

    fun showDialog(){
        viewModelScope.launch {
            delay(4000)

            alertStore.showToast("Alert after 4 seconds.")
        }
    }
}

为什么在我当前的实现中需要这个 @Inject 构造函数

class MainActivity : BaseActivity() {

  private lateinit var viewModel: MainViewModel

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    viewModel = getViewModel()
    viewModel.showDialog()
}

}

应用程序.kt

class App : DaggerApplication() {

override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
    return DaggerAppComponent.builder().addContext(this).build()
    }
}

应用组件.kt

@Component(
    modules = [
        AndroidInjectionModule::class,
        AppModule::class,
        ActivityBuilder::class,
        ViewModelInjector::class

    ]
)
@Singleton
interface AppComponent : AndroidInjector<App> {

    @Component.Builder
    interface Builder {

        fun addContext(@BindsInstance context: Context): Builder

        fun build(): AppComponent
    }
}

应用模块.kt

@Module
class AppModule {

    @Provides
    fun provideViewModelFactories(viewModels: Map<Class<out ViewModel>,
            @JvmSuppressWildcards Provider<ViewModel>>):
            ViewModelProvider.Factory {
        return object : ViewModelProvider.Factory {
            override fun <T : ViewModel?> create(modelClass: Class<T>): T {
                val factory = viewModels[modelClass]?.get() ?: error(
                    "No factory provided against ${modelClass.name}"
                )
                @Suppress("UNCHECKED_CAST")
                return factory as T
            }
        }
    }
}

ActivityBuilder.kt

@Module
abstract class ActivityBuilder {

    //@Scope("")
    @ContributesAndroidInjector ///(modules = {MainModelFactory.class})
    public abstract MainActivity bindMainActivity();
}

ViewModelInjector.kt

@Module 公共抽象类 ViewModelInjector {

@Binds
@IntoMap
@ViewModelKey(MainViewModel.class)
public abstract ViewModel providesMainViewModel(MainViewModel model);

}

ViewModelKey.kt

@MapKey
@Retention(AnnotationRetention.SOURCE)
annotation class ViewModelKey(
    val value: KClass<out ViewModel>
)

为什么我必须将@Inject 构造函数附加到每个ViewModel并请解释一下为什么我们需要@Binds @IntoMap和 ViewModel

标签: androiddagger-2android-architecture-componentsandroid-viewmodel

解决方案


当您使用dagger android时,您应该将您的活动和片段作为DaggerActivity(和DaggerFragment片段)的扩展。

class MainActivity : DaggerActivity() {

    @Inject
    lateinit var viewModel: MainViewModel
}

接下来,您应该为注入准备基础设施:

  1. 为您的每个活动创建注入器:

    // All your injectors can be defined in this module
    @Module(includes = [AndroidInjectionModule::class])
    interface AppInjectorModule {
    
        @ContributesAndroidInjector(modules = [MainActivityVmModule::class, /*other dependecies*/])
        fun getMainActivityInjector(): MainActivity
    }
    
  2. 创建模块以提供视图模型(一个活动可以有多个)和工厂

    @Module(includes = [VmFactoryModule::class])
    abstract class MainActivityVmModule {
    
        // bind implementation of ViewModel into map for ViewModelFactory
        @Binds
        @IntoMap
        @ClassKey(MainViewModelImpl::class)
        abstract fun bindMainVm(impl: MainViewModelImpl): ViewModel
    
        @Module
        companion object {
            @Provides
            @JvmStatic
            fun getMainVm(activity: MainActivity, factory: ViewModelProvider.Factory): MainViewModel {
                // create MainViewModelImpl in scope of MainActivity and inject dependecies by ViewModelFactory
                return ViewModelProviders.of(activity, factory)[MainViewModelImpl::class.java]
            }
        }
    }
    

    工厂可以由不同的模块提供,避免重复

    @Module
    interface VmFactoryModule {
    
        @Binds
        // bind your implementation of factory
        fun bindVmFactory(impl: ViewModelFactory): ViewModelProvider.Factory
    }
    
  3. 将活动注入器添加到AppComponent图表
    @Component(
        modules = [
            AppInjectorModule::class
        ]
    )
    @Singleton
    interface AppComponent : AndroidInjector<App>
    

附加信息:匕首和安卓


推荐阅读