[英]Intantiating a viewmodel using dagger2 in BaseFragment
I am sort of new to Dagger
and still learning it.我对Dagger
有点Dagger
并且仍在学习它。 According to the tutorials and blogs I read, currently Android does not have a way of injecting dependencies
into ViewModels
hence we need to use a custom ViewModelProvider.Factory
, that said, I managed to get my hands on this one根据我阅读的教程和博客,目前 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);
}
}
}
It works, it has worked for many of my use cases until now.它有效,直到现在它对我的许多用例都有效。 Originally I had to an instance of a ViewModel with something like this最初我不得不使用类似这样的 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);
}
}
But as the project grew I realized this was not neat, I had to do this in all my fragments so I opted for a different approach, I wanted to instantiate my ViewModel in my BaseFragment
instead and I did 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]);
}
} }
This gives me compile error这给了我编译错误
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
From the error message I can tell Dagger
is complaining the ViewModelProviderFactory
is being injected in the base
but used in the child
, I need help, is there a way to make this work?从错误消息中,我可以告诉Dagger
抱怨ViewModelProviderFactory
正在注入base
但在child
,我需要帮助,有没有办法使这项工作? surely I want to reduce on boilerplate and repetitive code.我当然想减少样板和重复代码。
I finally fixed it, not how I wanted but better than having to instantiate each viewmodel from the child class.我终于修复了它,不是我想要的,但比必须从子类实例化每个视图模型要好。 After reading this answer I came to a realization that this was not possible, so instead I removed @Inject
annotation from ViewModelProviderFactory
in my BaseFragment
and it looked like阅读此答案后,我意识到这是不可能的,因此我从BaseFragment
ViewModelProviderFactory
中删除了@Inject
注释,它看起来像
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();
}
}
And injected the provider
from the child fragments instead then called the setter from the BaseFragment
并从子片段注入provider
,然后从BaseFragment
调用 setter
public 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);
}
}
Key here is to call setProviderFactory(provider)
before super.onCreate(savedInstanceState)
because when super onCreate is called the provider should not be null, it should be set and ready to create the ViewModel
这里的关键是在super.onCreate(savedInstanceState)
setProviderFactory(provider)
之前调用setProviderFactory(provider)
因为当调用 super onCreate 时提供者不应该为空,它应该被设置并准备好创建ViewModel
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.