我是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后,我意识到这是不可能的,因此我从@InjectViewModelProviderFactory中删除​​了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中调用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);
    }

}


此处的关键是在setProviderFactory(provider)之前调用super.onCreate(savedInstanceState),因为在调用super onCreate时,提供程序不应为null,应将其设置并准备好创建ViewModel

关于java - 在BaseFragment中使用dagger2初始化 View 模型,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59836287/

10-10 14:53