ViewModelProviders.get(…) in base class

核能气质少年 提交于 2020-04-09 17:57:18

问题


I'm trying to cut some of Dagger's boilerplate by moving some of my ViewModel instantiation in an abstract base class but can't find quite a good way to do this. My intent is to instantiate all my ViewModels from my base fragment for them to be ready to consume by all child fragments without having them do their own instantiation. My issue lies in retrieving the ViewModel using a generic (VM)- specifically here: .get(viewModel::class.java) . I also attempted .get(VM::class.java) that is not permitted

BaseFragment

abstract class BaseFragment<VM : ViewModel> : Fragment() {

    @Inject lateinit var viewModelFactory: ViewModelProvider.Factory
    lateinit var viewModel : VM

    override fun onAttach(context: Context?) {
        super.onAttach(context)
        viewModel = ViewModelProviders.of(this, viewModelFactory).get(viewModel::class.java)
    }
}

ViewModelProviders.get(...) method signature

public <T extends ViewModel> T get(@NonNull Class<T> modelClass)

Is this even possible?


回答1:


If you have a BaseFragment defined like this:

public class BaseFragment<T extends ViewModel> extends Fragment {

    @Inject
    protected T viewModel;

}

You can use the ViewModelUtils class from the Architecture Components's Github browser sample to create ViewModel factories.

/**
 * Creates a one off view model factory for the given view model instance.
 */
public class ViewModelUtil {
    private ViewModelUtil() {}
    public static <T extends ViewModel> ViewModelProvider.Factory createFor(T model) {
        return new ViewModelProvider.Factory() {
            @Override
            public <T extends ViewModel> T create(Class<T> modelClass) {
                if (modelClass.isAssignableFrom(model.getClass())) {
                    return (T) model;
                }
                throw new IllegalArgumentException("unexpected model class " + modelClass);
            }
        };
    }
}

In the onActivityCreated method of the BaseFragment you can add

    @Override
    public void onActivityCreated(@Nullable final Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ViewModelProvider.Factory viewModelFactory = ViewModelUtil.createFor(viewModel);
        ViewModelProviders.of(this, viewModelFactory).get(viewModel.getClass());
    }

The code for this technique is from this blog post.




回答2:


val viewModel: T by lazy {
    ViewModelProviders.of(this).get(getTClass())
}

//获取泛型T的实际类型
private fun getTClass(): Class<T> {
    return (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
}



回答3:


I had the same issue but fix it with a bit of Reflection; I really don't like to use Reflection on Android, but didn't have other option.

private void initViewModel() {
    final Type[] types = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();
    mViewModel = ViewModelProviders.of(this).get((Class<M>) types[0]);
}

Hope this is useful to someone.



来源:https://stackoverflow.com/questions/48892551/viewmodelproviders-get-in-base-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!