例如,我正在使用Google示例-GithubBrowserSample(我使用的是Java版本,可以在here中找到)
要实现的目标:
我的活动(在我想要通过意图的情况下)模块:
@Module
public abstract class AddEditTaskModule {
@Provides
@Nullable
static String provideTaskId(AddEditTaskActivity activity) {
return activity.getIntent().getStringExtra(AddEditTaskActivity.EDIT_TASK_ID);
}
@ContributesAndroidInjector
abstract AddEditTaskFragment contributeAddEditTaskFragment();
@Binds
@IntoMap
@ViewModelScope(AddEditTaskViewModel.class)
abstract ViewModel bindAddEditTaskViewModel(AddEditTaskViewModel viewModel);
}
我的工厂:
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) {
Timber.d("@Inject constructor");
this.creators = creators;
}
@SuppressWarnings("unchecked")
@NonNull
@Override
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) {
Timber.e(e);
throw new RuntimeException(e);
}
}
}
在Google的示例中-工厂标注为@Singleton
在我的情况下-我不能将其作为单例使用,因为我需要在活动模块中绑定viewModel-从那里传递intentExtra。
如此,它正在工作,但是每次我尝试通过以下方式获取viewModel时
@Inject
ViewModelProvider.Factory viewModelFactory;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
AddEditTaskViewModel mViewModel =
ViewModelProviders.of(this, viewModelFactory).get(AddEditTaskViewModel.class);
}
新工厂已创建。
这对我来说似乎很不对劲。 我在做什么错了?
编辑:
解决方案1:
为此类型的ViewModel创建自定义工厂
public class AddEditTaskViewModel extends ViewModel {
...
public static class Factory extends ViewModelProvider.NewInstanceFactory{
@NonNull
private final Application application;
@Nullable
private final String taskId;
@Inject
public Factory(@NonNull Application application, @Nullable String taskId){
Timber.d("Constructor");
this.application = application;
this.taskId = taskId;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection unchecked
return (T) new AddEditTaskViewModel(application, taskId);
}
}
}
ActivtyModule:
@Module
public abstract class AddEditTaskModule {
@Provides
@Nullable
static String provideTaskId(AddEditTaskActivity activity) {
return activity.getIntent().getStringExtra(AddEditTaskActivity.EDIT_TASK_ID);
}
@ContributesAndroidInjector
abstract AddEditTaskFragment contributeAddEditTaskFragment();
@Provides
static AddEditTaskViewModel.Factory bindAddEditTaskViewModelFactory(Application application, @Nullable String id){
return new AddEditTaskViewModel.Factory(application, id);
}
}
现在,在我们的活动中使用,并在此活动中使用片段:
@Inject
AddEditTaskViewModel.Factory viewModelFactory;
AddEditTaskViewModel mViewModel =
ViewModelProviders.of(this, viewModelFactory).get(AddEditTaskViewModel.class);
这也可以。问题是,在这种情况下,我需要复制ViewModel的构造函数3(!!)次:1.在dagger模块中,2.在工厂中,3.在viewModel中。
我不能只使用:
@Binds
abstract AddEditTaskViewModel.Factory bindAddEditTaskViewModelFactory(AddEditTaskViewModel.Factory factory);
因为-循环依赖
这个解决方案对我来说似乎很糟糕。
解决方案2:
使用两个ViewModelFactory。
第一个全局变量,用@Singleton
注释。 Witch将用于ViewModel,而不会出现“向ViewModel中额外注入意图”之类的情况。
第二个-应付ViewModelFactory,但没有@Singleton
批注。应该在带有@Named
标签的活动模块中使用,例如:
@Binds
@Named(AddEditTaskViewModel.TAG)
abstract ViewModelProvider.Factory bindAddEditTaskViewModelFactory(ViewModelFactoryTarget factory);
活动/片段:
@Inject
@Named(AddEditTaskViewModel.TAG)
ViewModelProvider.Factory viewModelFactory;
好吧,这似乎是以上任何一种更好的解决方案,除了几个“ but”:
@Singleton
注释不同。@Named
不太好。答案 0 :(得分:0)
调整后,它比我想象的要简单。
对于常见的ViewModel:
从ViewModelFactory中删除@Singleton
注释。
将@Singleton
添加到 ViewModelModule
@Binds
@Singleton
abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory factory);
对于ViewModels,我们需要使用Intent Extra注入:
ActivityModule:
@Provides
@Nullable
static String provideTaskId(AddEditTaskActivity activity) {
return activity.getIntent().getStringExtra(AddEditTaskActivity.EDIT_TASK_ID);
}
@Binds
@Named(AddEditTaskViewModel.TAG)
abstract ViewModelProvider.Factory bindAddEditTaskViewModelFactory(ViewModelFactory factory);
比在活动/片段中使用
@Inject
@Named(AddEditTaskViewModel.TAG)
ViewModelProvider.Factory viewModelFactory;