Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ViewModelProviders.get(...) in base class

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?

like image 583
ben Avatar asked Feb 20 '18 19:02

ben


2 Answers

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.

like image 162
LordRaydenMK Avatar answered Oct 24 '22 08:10

LordRaydenMK


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>
}
like image 40
王大大 Avatar answered Oct 24 '22 08:10

王大大