如何使 MVRX 视图模型与 dagger2 一起工作?
How to make MVRX viewmodels work with dagger2?
编辑
添加@ViewModelKey 并确保所有视图模型都具有@Inject 注释就可以了
使用 Dagger2 Di 库和 ViewModelFactory 注入 ViewModel 导致缺少绑定构建错误。
我收到的错误如下:
AppComponent.java:12: 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.honing.daggerexploration.DaggerExplorationApplication> {
^
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
com.honing.daggerexploration.di.DaggerViewModelFactory(creators)
com.honing.daggerexploration.di.DaggerViewModelFactory is injected at
com.honing.daggerexploration.features.MainActivity.viewModelFactory
com.honing.daggerexploration.features.MainActivity is injected at
dagger.android.AndroidInjector.inject(T) [com.honing.daggerexploration.di.AppComponent → com.honing.daggerexploration.di.modules.ActivityModule_BindActivityMain.MainActivitySubcomponent]
我搜索了其他 Whosebug 问题,但没有一个能为我解决问题。
我用的Dagger版本是最新的,2.22.1
我打赌这个错误与 MVRX 无关,因为我能够在不涉及 mvrx 视图模型的情况下在小型库中重现它 class,但是我的意图是最终将 dagger2 与 mvrx 框架一起使用并且能够向其注入依赖项。
一些与此相关的代码:
DaggerExplorationApplication
class DaggerExplorationApplication : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().create(this)
}
}
DaggerViewModelFactory:
/**
* ViewModelFactory which uses Dagger to create the instances.
*/
class DaggerViewModelFactory @Inject constructor(
private val creators: @JvmSuppressWildcards Map<Class<out ViewModel>, Provider<ViewModel>>
) : ViewModelProvider.Factory {
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 {
@Suppress("UNCHECKED_CAST")
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
ViewModelFactoryModule
@Module
abstract class ViewModelFactoryModule {
@Binds
abstract fun bindViewModelFactory(viewModelFactory: DaggerViewModelFactory): ViewModelProvider.Factory
}
ActivityModule
@Module
abstract class ActivityModule {
@ContributesAndroidInjector(modules = [ViewModelFactoryModule::class])
abstract fun bindActivityMain(): MainActivity
}
关于我用dagger 实现mvrx 的努力,据此我需要使用square 的AssistedInject 库,我看了视频,比较明白这背后的原因。然而,由于上述错误,我无法构建项目。 chrisbanes 的一个有趣话题也在 this link
上
chrisbanes 使用 this project(Tivi) 成功实现了带有 dagger2 的 MVRX ViewModels,我尝试按照他们所做的进行操作,但我也失败了。 post 顶部描述的问题阻碍了我。准备提供任何缺失的代码,如果需要能够解决此问题的更多信息。
这就是我使用 MVVM 架构使我的 Android 应用程序与 Dagger 2 一起工作的方式。它是 Java,但我希望它无论如何都能引导您朝着正确的方向前进。
AppComponent
@ApplicationScope
@Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBuilder.class
})
public interface AppComponent {
void inject(MyApp app);
@Component.Builder
interface Builder {
@BindsInstance
Builder application(Application application);
AppComponent build();
}
}
ViewModelFactory
@ApplicationScope
public class ViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
@Inject
public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
this.creators = creators;
}
@SuppressWarnings("unchecked")
@Override
@NonNull
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
Provider<? extends ViewModel> creator = creators.get(modelClass);
if (creator == null) {
for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
if (modelClass.isAssignableFrom(entry.getKey())) {
creator = entry.getValue();
break;
}
}
}
if (creator == null) {
throw new IllegalArgumentException("unknown model class " + modelClass);
}
try {
return (T) creator.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
ViewModelModule
@Module
public abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(MainActivityViewModel.class)
abstract ViewModel bindMainActivityViewModel(MainActivityViewModel mainActivityViewModel);
// Same thing for each view model
...
}
Activity建设者
@Module
public abstract class ActivityBuilder {
@ContributesAndroidInjector(modules = {
MainActivityModule.class
// Add the Provider of each child fragment's viewmodel.
})
public abstract MainActivity bindMainActivity();
// Same for each new activity
}
ViewModelKey
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@MapKey
public @interface ViewModelKey {
Class<? extends ViewModel> value();
}
应用范围
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}
AppModule
@Module(includes = {
ViewModelModule.class,
})
public class AppModule {
// Provides all the things needed for the whole application, such as Daos, Retrofit interface, contexts, etc.
}
MyApp
public class MyApp extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
@Override
public void onCreate() {
DaggerAppComponent
.builder()
.application(this)
.build()
.inject(this);
super.onCreate();
}
@Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
}
主要Activity
public class MainActivity extends AppCompatActivity {
@Inject
ViewModelProvider.Factory mViewModelFactory;
private MainActivityViewModel mViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewModel = ViewModelProviders.of(this, mViewModelFactory).get(MainActivityViewModel.class);
...
}
}
主要Activity模块
@Module
public class MainActivityModule {
//Provides all the things needed just for your MainActivity
}
主要ActivityViewModel
public class MainActivityViewModel extends ViewModel {
// Member variables
@Inject
public MainActivityViewModel(... things to inject into the viewmodel, such as daos, repositories, contexts, etc. ... ) {
...
}
}
所以,看起来配置很多,但是一旦完成初始设置,在此基础上进行构建会更容易。
总结一下:
- 创建注释
@ApplicationScope
(或使用默认的 @Singleton
),更重要的是,创建 @ViewModelKey
注释。
- 使您的应用程序 class 实现
HasActivityInjector
并使用 DaggerAppComponent
生成器进行注入。
- 完全按照我之前提到的那样实施
AppComponent
和 ViewModelFactory
。
- 定义您的
AppModule
以提供您的应用所需的所有内容。但请记住包含 ViewModelModule
,因为它负责提供 ViewModel。
- 每次你想添加一个新的 activity 和它自己的 ViewModel 时,你必须执行以下操作:
- 创建
Activity
、ActivityModule
和 ActivityViewModel
。
- 在
ViewModelModule
中添加一个条目以绑定视图模型。
- 在
ActivityBuilder
中添加条目以提供 Activity。
- 尽情享受吧。
您缺少地图多重绑定的配置。
Tivi 有 @ViewModelKey
:
/*
* Copyright 2017 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package app.tivi.inject
import androidx.lifecycle.ViewModel
import dagger.MapKey
import kotlin.reflect.KClass
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
并且它有一个模块,将 ViewModelKey 绑定到 ViewModel 的特定子类型,以这样的方式公开为 ViewModel
(并用键标记):
/*
* Copyright 2017 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@Binds
@IntoMap
@ViewModelKey(PopularShowsViewModel::class)
abstract fun bindPopularShowsViewModel(viewModel: PopularShowsViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(TrendingShowsViewModel::class)
abstract fun bindTrendingShowsViewModel(viewModel: TrendingShowsViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(ShowDetailsNavigatorViewModel::class)
abstract fun bindDetailsNavigatorViewModel(viewModel: ShowDetailsNavigatorViewModel): ViewModel
因此您需要将这些多绑定配置设置为使用模块的组件。
同样重要的是,他们的 ViewModel 类 具有 @Inject
带注释的构造函数才能正常工作。
编辑
添加@ViewModelKey 并确保所有视图模型都具有@Inject 注释就可以了
使用 Dagger2 Di 库和 ViewModelFactory 注入 ViewModel 导致缺少绑定构建错误。
我收到的错误如下:
AppComponent.java:12: 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.honing.daggerexploration.DaggerExplorationApplication> {
^
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
com.honing.daggerexploration.di.DaggerViewModelFactory(creators)
com.honing.daggerexploration.di.DaggerViewModelFactory is injected at
com.honing.daggerexploration.features.MainActivity.viewModelFactory
com.honing.daggerexploration.features.MainActivity is injected at
dagger.android.AndroidInjector.inject(T) [com.honing.daggerexploration.di.AppComponent → com.honing.daggerexploration.di.modules.ActivityModule_BindActivityMain.MainActivitySubcomponent]
我搜索了其他 Whosebug 问题,但没有一个能为我解决问题。
我用的Dagger版本是最新的,2.22.1
我打赌这个错误与 MVRX 无关,因为我能够在不涉及 mvrx 视图模型的情况下在小型库中重现它 class,但是我的意图是最终将 dagger2 与 mvrx 框架一起使用并且能够向其注入依赖项。
一些与此相关的代码:
DaggerExplorationApplication
class DaggerExplorationApplication : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerAppComponent.builder().create(this)
}
}
DaggerViewModelFactory:
/**
* ViewModelFactory which uses Dagger to create the instances.
*/
class DaggerViewModelFactory @Inject constructor(
private val creators: @JvmSuppressWildcards Map<Class<out ViewModel>, Provider<ViewModel>>
) : ViewModelProvider.Factory {
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 {
@Suppress("UNCHECKED_CAST")
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
ViewModelFactoryModule
@Module
abstract class ViewModelFactoryModule {
@Binds
abstract fun bindViewModelFactory(viewModelFactory: DaggerViewModelFactory): ViewModelProvider.Factory
}
ActivityModule
@Module
abstract class ActivityModule {
@ContributesAndroidInjector(modules = [ViewModelFactoryModule::class])
abstract fun bindActivityMain(): MainActivity
}
关于我用dagger 实现mvrx 的努力,据此我需要使用square 的AssistedInject 库,我看了视频,比较明白这背后的原因。然而,由于上述错误,我无法构建项目。 chrisbanes 的一个有趣话题也在 this link
上chrisbanes 使用 this project(Tivi) 成功实现了带有 dagger2 的 MVRX ViewModels,我尝试按照他们所做的进行操作,但我也失败了。 post 顶部描述的问题阻碍了我。准备提供任何缺失的代码,如果需要能够解决此问题的更多信息。
这就是我使用 MVVM 架构使我的 Android 应用程序与 Dagger 2 一起工作的方式。它是 Java,但我希望它无论如何都能引导您朝着正确的方向前进。
AppComponent
@ApplicationScope
@Component(modules = {
AndroidInjectionModule.class,
AppModule.class,
ActivityBuilder.class
})
public interface AppComponent {
void inject(MyApp app);
@Component.Builder
interface Builder {
@BindsInstance
Builder application(Application application);
AppComponent build();
}
}
ViewModelFactory
@ApplicationScope
public class ViewModelFactory implements ViewModelProvider.Factory {
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
@Inject
public ViewModelFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
this.creators = creators;
}
@SuppressWarnings("unchecked")
@Override
@NonNull
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
Provider<? extends ViewModel> creator = creators.get(modelClass);
if (creator == null) {
for (Map.Entry<Class<? extends ViewModel>, Provider<ViewModel>> entry : creators.entrySet()) {
if (modelClass.isAssignableFrom(entry.getKey())) {
creator = entry.getValue();
break;
}
}
}
if (creator == null) {
throw new IllegalArgumentException("unknown model class " + modelClass);
}
try {
return (T) creator.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
ViewModelModule
@Module
public abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(MainActivityViewModel.class)
abstract ViewModel bindMainActivityViewModel(MainActivityViewModel mainActivityViewModel);
// Same thing for each view model
...
}
Activity建设者
@Module
public abstract class ActivityBuilder {
@ContributesAndroidInjector(modules = {
MainActivityModule.class
// Add the Provider of each child fragment's viewmodel.
})
public abstract MainActivity bindMainActivity();
// Same for each new activity
}
ViewModelKey
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@MapKey
public @interface ViewModelKey {
Class<? extends ViewModel> value();
}
应用范围
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationScope {
}
AppModule
@Module(includes = {
ViewModelModule.class,
})
public class AppModule {
// Provides all the things needed for the whole application, such as Daos, Retrofit interface, contexts, etc.
}
MyApp
public class MyApp extends Application implements HasActivityInjector {
@Inject
DispatchingAndroidInjector<Activity> activityDispatchingAndroidInjector;
@Override
public void onCreate() {
DaggerAppComponent
.builder()
.application(this)
.build()
.inject(this);
super.onCreate();
}
@Override
public DispatchingAndroidInjector<Activity> activityInjector() {
return activityDispatchingAndroidInjector;
}
}
主要Activity
public class MainActivity extends AppCompatActivity {
@Inject
ViewModelProvider.Factory mViewModelFactory;
private MainActivityViewModel mViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewModel = ViewModelProviders.of(this, mViewModelFactory).get(MainActivityViewModel.class);
...
}
}
主要Activity模块
@Module
public class MainActivityModule {
//Provides all the things needed just for your MainActivity
}
主要ActivityViewModel
public class MainActivityViewModel extends ViewModel {
// Member variables
@Inject
public MainActivityViewModel(... things to inject into the viewmodel, such as daos, repositories, contexts, etc. ... ) {
...
}
}
所以,看起来配置很多,但是一旦完成初始设置,在此基础上进行构建会更容易。
总结一下:
- 创建注释
@ApplicationScope
(或使用默认的@Singleton
),更重要的是,创建@ViewModelKey
注释。 - 使您的应用程序 class 实现
HasActivityInjector
并使用DaggerAppComponent
生成器进行注入。 - 完全按照我之前提到的那样实施
AppComponent
和ViewModelFactory
。 - 定义您的
AppModule
以提供您的应用所需的所有内容。但请记住包含ViewModelModule
,因为它负责提供 ViewModel。 - 每次你想添加一个新的 activity 和它自己的 ViewModel 时,你必须执行以下操作:
- 创建
Activity
、ActivityModule
和ActivityViewModel
。 - 在
ViewModelModule
中添加一个条目以绑定视图模型。 - 在
ActivityBuilder
中添加条目以提供 Activity。 - 尽情享受吧。
- 创建
您缺少地图多重绑定的配置。
Tivi 有 @ViewModelKey
:
/*
* Copyright 2017 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package app.tivi.inject
import androidx.lifecycle.ViewModel
import dagger.MapKey
import kotlin.reflect.KClass
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
并且它有一个模块,将 ViewModelKey 绑定到 ViewModel 的特定子类型,以这样的方式公开为 ViewModel
(并用键标记):
/*
* Copyright 2017 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@Binds
@IntoMap
@ViewModelKey(PopularShowsViewModel::class)
abstract fun bindPopularShowsViewModel(viewModel: PopularShowsViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(TrendingShowsViewModel::class)
abstract fun bindTrendingShowsViewModel(viewModel: TrendingShowsViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(ShowDetailsNavigatorViewModel::class)
abstract fun bindDetailsNavigatorViewModel(viewModel: ShowDetailsNavigatorViewModel): ViewModel
因此您需要将这些多绑定配置设置为使用模块的组件。
同样重要的是,他们的 ViewModel 类 具有 @Inject
带注释的构造函数才能正常工作。