Dagger无法使用KClass注入ViewModel

时间:2019-04-24 15:01:39

标签: mvvm kotlin dependency-injection viewmodel dagger-2

我将项目Java移至kotlin,但对KClassClass感到困惑

这是我的BaseActivity

abstract class BaseActivity<DB : ViewDataBinding, VM : BaseViewModel> : DaggerAppCompatActivity() {

    private lateinit var mCustomDialog: CustomDialog
    private lateinit var mViewDataBinding: DB
    private lateinit var mViewModel : VM

    @Inject
    lateinit var viewModelFactory: ViewModelFactory

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Set Custom Dialog
        mCustomDialog = CustomDialog(this, R.style.LoadingDialogStyle)
        // Set ViewModel
        mViewModel = ViewModelProviders.of(this, viewModelFactory).get(getViewModelClass().java)
        // Set DataBinding
        mViewDataBinding = DataBindingUtil.setContentView(this, getLayoutId())
        mViewDataBinding.lifecycleOwner = this
        mViewDataBinding.setVariable(getBindingVariable(), mViewModel)
        mViewDataBinding.executePendingBindings()
        // Initialize UI
        prepareView(savedInstanceState)
    }


    @LayoutRes
    abstract fun getLayoutId(): Int


    protected abstract fun getViewModelClass(): KClass<VM>


    abstract fun getBindingVariable(): Int


    fun getViewModel(): VM {
        return mViewModel
    }


    fun getViewDataBinding() : DB {
        return mViewDataBinding
    }

我正在使用protected abstract fun getViewModelClass(): KClass<VM>函数在下面的函数中初始化ViewModel

ViewModelProviders.of(this, viewModelFactory).get(getViewModelClass().java)

我通过这种方式在活动中使用ViewModel

class SplashActivity : BaseActivity<ActivitySplashBinding, SplashViewModel>() {

    override fun getViewModelClass(): KClass<SplashViewModel> {
        return SplashViewModel::class
    }

    override fun getLayoutId(): Int {
        return R.layout.activity_splash
    }

    override fun getBindingVariable(): Int {
        return BR.vm
    }

    override fun prepareView(savedInstanceState: Bundle?) {
        getViewModel().testLog()
    }

}

但是当我运行项目时,出现了这个错误

error: [Dagger/MissingBinding] java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.
public abstract interface AppComponent extends dagger.android.AndroidInjector<com.example.example.MyApp> {
                ^
      java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
          com.example.example.utils.ViewModelFactory(viewModels)
      com.example.example.utils.ViewModelFactory is injected at
          com.example.example.base.BaseActivity.viewModelFactory
      com.example.example.ui.splash.SplashActivity is injected at
          dagger.android.AndroidInjector.inject(T) [com.example.example.di.AppComponent ? com.example.example.di.ActivityBindingsModule_SplashActivityInjector$app_debug.SplashActivitySubcomponent]

所以我做了一些研究,发现在我的KClass中是关于ViewModelKey

这里是ViewModelKey

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

如果我不将代码更改为Kotlin并使用像这样的旧Java类,它将正常工作

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@MapKey
public @interface ViewModelKey {
    Class<? extends ViewModel> value();
}

这是我的ViewModelFactory

@Suppress("UNCHECKED_CAST")
class ViewModelFactory @Inject
constructor(private val viewModels: MutableMap<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>) : ViewModelProvider.Factory {

    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        val creator = viewModels[modelClass]
            ?: viewModels.asIterable().firstOrNull { modelClass.isAssignableFrom(it.key) }?.value
            ?: throw IllegalArgumentException("unknown model class $modelClass")
        return try {
            creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }
    }
}

我的SplashActivityModule

@Module
abstract class SplashActivityModule {

    @Binds
    @IntoMap
    @ViewModelKey(SplashViewModel::class)
    internal abstract fun provideSplashViewModel(splashViewModel: SplashViewModel) : ViewModel

}

那么我该如何正确地将ViewModelKey与Kotlin一起使用以及导致此错误的主要原因是什么,我们将不胜感激

2 个答案:

答案 0 :(得分:0)

  

您的ViewModelKey就像

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

答案 1 :(得分:0)

如上所述this question的问题与Kotlin版本有关。使用高于1.3.30的版本可以解决此问题。