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?
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.
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>
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With