Dagger 无法使用 KClass 注入 ViewModel
Dagger can not inject ViewModel with KClass
我正在将我的项目 java 转移到 kotlin,但对 KClass
和 Class
有一些困惑
这是我的 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
class
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 class 它可以正常工作
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@MapKey
public @interface ViewModelKey {
Class<? extends ViewModel> value();
}
这是我的ViewModelFactory
class
@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
}
那么如何在 Kotlin 中正确使用 ViewModelKey
以及此错误的主要原因是什么,我们将不胜感激
Your ViewModelKey be like
@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>)
如上所述 问题与 Kotlin 版本有关。使用1.3.30以上版本解决问题
我正在将我的项目 java 转移到 kotlin,但对 KClass
和 Class
这是我的 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
class
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 class 它可以正常工作
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@MapKey
public @interface ViewModelKey {
Class<? extends ViewModel> value();
}
这是我的ViewModelFactory
class
@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
}
那么如何在 Kotlin 中正确使用 ViewModelKey
以及此错误的主要原因是什么,我们将不胜感激
Your ViewModelKey be like
@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>)
如上所述