Android dagger2使用Factory将intent注入viewModel

问题描述 投票:0回答:1

作为一个例子我使用谷歌样本 - GithubBrowserSample(我使用java版本,它可以找到here

想要实现的目标:

  1. 使用intent extra启动活动
  2. 将extra注入viewModel构造函数

我的活动(我希望传递意图的女巫)模块:

@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);
        }
    }
}

在谷歌的例子中 - 工厂注释为@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将用于ViewModels而不会出现“将意图注入ViewModel”等情况。

第二个 - 应对ViewModelFactory,但没有@Singleton注释。应该在带有@Named标签的活动模块中使用,如下所示:

@Binds
@Named(AddEditTaskViewModel.TAG)
abstract ViewModelProvider.Factory bindAddEditTaskViewModelFactory(ViewModelFactoryTarget factory);

在活动/片段:

@Inject
@Named(AddEditTaskViewModel.TAG)
ViewModelProvider.Factory viewModelFactory;

好吧,这似乎是上面任何一个更好的解决方案,除了几个“但是”:

  1. 我有两个完全相同的ViewModelFactory类,只有@Singleton注释才有所不同。
  2. 使用@Named对我来说不是很优雅。
java android dagger-2 android-architecture-components android-viewmodel
1个回答
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;
© www.soinside.com 2019 - 2024. All rights reserved.