我是Dagger
的新手,仍然在学习它。根据我阅读的教程和博客,当前Android无法将dependencies
注入ViewModels
,因此我们需要使用自定义的ViewModelProvider.Factory
,也就是说,我设法掌握了这一点。
public class ViewModelProviderFactory implements ViewModelProvider.Factory {
private static final String TAG = ViewModelProviderFactory.class.getSimpleName();
private final Map<Class<? extends ViewModel>, Provider<ViewModel>> creators;
@Inject
public ViewModelProviderFactory(Map<Class<? extends ViewModel>, Provider<ViewModel>> creators) {
this.creators = creators;
}
@Override
public <T extends ViewModel> T create(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);
}
}
}
它有效,到目前为止,它已经在我的许多用例中起作用。本来我必须有一个像这样的ViewModel实例
public AFragment extends BaseFragment{
@Inject
ViewModelProviderFactory providerFactory;
private MyViewModel viewModel;
MyViewModel getViewModel(){
return ViewModelProviders.of(this, providerFactory).get(MyViewModel.class);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = getViewModel();
tokenAuthenticator.setAuthenticatorListener(this);
}
}
但是随着项目的发展,我意识到这并不整洁,我必须在所有片段中都做到这一点,所以我选择了另一种方法,我想在我的
BaseFragment
中实例化ViewModel,而我做到了public abstract class BaseFragment<T extends BaseViewModel, E extends ViewDataBinding> extends DaggerFragment implements TokenAuthenticator.AuthenticatorListener {
private static final String TAG = BaseFragment.class.getSimpleName();
public E binding;
public final CompositeDisposable disposables = new CompositeDisposable();
public T viewModel;
@Inject
ViewModelProviderFactory providerFactory;
private int layoutId;
/**
* @return view model instance
*/
public T getViewModel() {
final Type[] types = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();
return ViewModelProviders.of(this, providerFactory).get((Class<T>)types[0]);
}
}
这给我编译错误
A binding with matching key exists in component: xxx.xxx.core.base.dagger.builders.FragmentBuilderModule_ContributeDeliveryPlanFragment.DeliveryPlanFragmentSubcomponent
.
.
.
A binding with matching key exists in component: xxx.xxx.core.base.dagger.builders.FragmentBuilderModule_ContributePayWithMtmMobileMoneyFragment.PayWithMtmMobileMoneyFragmentSubcomponent
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
xxx.xxx.core.base.ViewModelProviderFactory(creators)
xxx.xxx.core.base.ViewModelProviderFactory is injected at
mika.e.mikaexpressstore.core.base.BaseFragment.providerFactory
xxx.xxx.xxx.xxx.cashondelivery.CashOnDeliveryFragment is injected at
dagger.android.AndroidInjector.inject(T) [xxx.xxx.core.base.dagger.component.AppComponent → xxx.xxx.core.base.dagger.builders.FragmentBuilderModule_ContributeCashOnDeliveryFragment.CashOnDeliveryFragmentSubcomponent]
2 errors
从错误消息中,我可以告诉
Dagger
正在抱怨ViewModelProviderFactory
被注入到base
中但在child
中使用了,我需要帮助,有没有办法使这项工作呢?当然,我想减少样板代码和重复代码。 最佳答案
我终于解决了它,不是我想要的,而是比必须从子类实例化每个视图模型更好。阅读此answer后,我意识到这是不可能的,因此我从@Inject
的ViewModelProviderFactory
中删除了BaseFragment
批注,它看起来像
public abstract class BaseFragment<T extends BaseViewModel, E extends ViewDataBinding> extends DaggerFragment implements TokenAuthenticator.AuthenticatorListener {
private static final String TAG = BaseFragment.class.getSimpleName();
public E binding;
public final CompositeDisposable disposables = new CompositeDisposable();
public T viewModel;
@Inject
private ViewModelProviderFactory providerFactory;
private int layoutId;
/**
* @return view model instance
*/
public T getViewModel() {
final Type[] types = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();
return ViewModelProviders.of(this, providerFactory).get((Class<T>)types[0]);
}
@MainThread
protected final void setProviderFactory(ViewModelProviderFactory providerFactory) {
this.providerFactory = providerFactory;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = getViewModel();
}
}
然后从子片段中注入
provider
,然后从BaseFragment
中调用setterpublic AFragment extends BaseFragment{
@Inject
ViewModelProviderFactory providerFactory;
private MyViewModel viewModel;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
setLayoutId(R.layout.layout_layout);
setProviderFactory(providerFactory);
super.onCreate(savedInstanceState);
}
}
此处的关键是在
setProviderFactory(provider)
之前调用super.onCreate(savedInstanceState)
,因为在调用super onCreate时,提供程序不应为null,应将其设置并准备好创建ViewModel
关于java - 在BaseFragment中使用dagger2初始化 View 模型,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59836287/