一、什么是ViewModel
ViewModel顾名思义,是以感知生命周期的形式来存储和管理视图相关的数据。ViewModel主要有以下的特点:
1.当Activity被销毁时,我们可以使用onSaveInstanceState()方法恢复其数据,这种方法仅适用于恢复少量的支持序列化、反序列化的数据,不适用于大量数据,如用户列表或位图。而ViewModel不仅支持大量数据,还不需要序列化、反序列化操作。
2.Activity/Fragment(视图控制器)主要用于显示视图数据,如果它们也负责数据库或者网络加载数据等操作,那么一旦逻辑过多,会导致视图控制器臃肿,ViewModel可以更容易,更有效的将视图数据相关逻辑和视图控制器分离开来。
3.视图控制器经常需要一些时间才可能返回的异步调用,视图控制器需要管理这些调用,在合适的时候清理它们,以确保它们的生命周期不会大于自身,避免内存泄漏。而ViewModel恰恰可以避免内存泄漏的发生
二、如何使用ViewModel
1 定义一个TesModel数据类
public class TesModel {
private String age;
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public String toString() {
return "TesModel{" +
"age=" + age +
'}';
}
}
2.定义一个TesViewModel数据类
public class TestViewModel extends ViewModel {
public final MutableLiveData<TesModel> mUserLiveData = new MutableLiveData<>();
public TestViewModel() {
mUserLiveData.postValue(new TesModel("25"));
}
public void doSomething() {
TesModel tesModel = mUserLiveData.getValue();
if (tesModel != null) {
mUserLiveData.setValue(tesModel);
}
}
}
这时候在Activity中就可以使用ViewModel了
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private TextView testView;
private TestViewModel testViewModel ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
testView = findViewById(R.id.testView);
testViewModel = ViewModelProviders.of(this).get(TestViewModel.class);
testViewModel.mUserLiveData.observe(this, new Observer<TesModel>() {
@Override
public void onChanged(TesModel tesModel) {
testView.setText(tesModel.toString());
}
});
testView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
testViewModel.doSomething();
}
}
这个时候,我们点击一下按钮(user中的age变为25),我们可以旋转手机屏幕(这个时候其实Activity是重新创建了,也就是onCreate()方法被再次调用,但是ViewModel其实是没有重新创建的,还是之前那个ViewModel),但是当我们旋转之后,发现TextView上显示的age居然还是25 这个就不得不提ViewModel的生命周期了,它只有在Activity销毁之后,它才会自动销毁(所以别让ViewModel持有Activity引用啊,会内存泄露的). 下面引用一下谷歌官方的图片,将ViewModel的生命周期展示的淋漓尽致.
3. ViewModel妙用1: Activity与Fragment"通信"
有了ViewModel,Activity与Fragment可以共享一个ViewModel,因为Fragment是依附在Activity上的,在实例化ViewModel时将该Activity传入ViewModelProviders,它会给你一个该Activity已创建好了的ViewModel,这个Fragment可以方便的访问该ViewModel中的数据.在Activity中修改userModel数据后,该Fragment就能拿到更新后的数据.
public class MyFragment extends Fragment {
public void onStart() {
//这里拿到的ViewModel实例,其实是和Activity中创建的是一个实例
TestViewModel = ViewModelProviders.of(getActivity()).get(TestViewModel.class);
}
}
4. ViewModel妙用2: Fragment与Fragment"通信"
(Google官方例子)
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
public class DetailFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// Update the UI.
});
}
}
首先定义一个ViewModel,在里面放点数据
然后在MasterFragment和DetailFragment都可以拿到该ViewModel,拿到了该ViewModel就可以拿到里面的数据了,相当于间接通过ViewModel通信了
二、ViewModel源码解析
testViewModel = ViewModelProviders.of(this).get(TestViewModel.class);
1. ViewModelProviders.of(this) 方法
/**
* 用于构建一个ViewModelProvider,当Activity是alive时它会保留所有的该Activity对应的ViewModels.
*/
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
//检查application是否为空,不为空则接收
Application application = checkApplication(activity);
if (factory == null) {
//构建一个ViewModelProvider.AndroidViewModelFactory
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(activity.getViewModelStore(), factory);
}
创建 ViewModelProvider
ViewModelProviders里面的of()函数其实是为了方便我们构建一个ViewModelProvider.而ViewModelProvider,一看名字就知道干啥的了,就是提供ViewModel的.
Factory是ViewModelProvider的一个内部接口,它的实现类是拿来构建ViewModel实例的.它里面只有一个方法,就是创建一个ViewModel.
public interface Factory {
/**
* Creates a new instance of the given {@code Class}.
* <p>
*
* @param modelClass a {@code Class} whose instance is requested
* @param <T> The type parameter for the ViewModel.
* @return a newly created ViewModel
*/
@NonNull
<T extends ViewModel> T create(@NonNull Class<T> modelClass);
}
Factory有2个实现类:一个是NewInstanceFactory, 一个是AndroidViewModelFactory
public static class NewInstanceFactory implements Factory {
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
}
AndroidViewModelFactory 源码
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
public AndroidViewModelFactory(@NonNull Application application) {
mApplication = application;
}
@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
//noinspection TryWithIdenticalCatches
try {
return modelClass.getConstructor(Application.class).newInstance(mApplication);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InstantiationException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
} catch (InvocationTargetException e) {
throw new RuntimeException("Cannot create an instance of " + modelClass, e);
}
}
return super.create(modelClass);
}
}
AndroidViewModelFactory专门用来实例化那种构造方法里面有参数的class,并且ViewModel里面可能是带Context的.
AndroidViewModelFactory通过构造方法给ViewModel带入Application,就可以在ViewModel里面拿到Context,因为Application是APP全局的,那么不存在内存泄露的问题.完美解决了有些ViewModel里面需要Context引用,但是又担心内存泄露的问题.
getViewModelStore()方法(这个方法会返回ViewModelStore,这个类是拿来存储ViewModel的,下面会说到),这里的activity是androidx.fragment.app.FragmentActivity,看一下这个getViewModelStore()方法
ViewModelProvider(activity.getViewModelStore(), factory);
/**
* 获取这个Activity相关联的ViewModelStore
*/
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
+ "Application instance. You can't request ViewModel before onCreate call.");
}
if (mViewModelStore == null) {
//获取最近一次横竖屏切换时保存下来的数据
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
//没想到吧,Activity在横竖屏切换时悄悄保存了viewModelStore
//注意,这是FragmentActivity中的NonConfigurationInstances(其实Activity中还定义了一个NonConfigurationInstances,内容要比这个多一些,但是由于没有关系到它,这里就不提及了)
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
FragmentManagerNonConfig fragments;
}
在看看这个两个方法
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
Activity.Destroy()时保存了ViewModelStore,在Activity启动时调用的Activity.attach()中恢复。而ViewModelStore内部维护HashMap存储ViewModel实例。
在看ViewModelStore
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}
大体思路是利用一个key来缓存ViewModel,有缓存则用缓存的,没有则重新构建.构建时使用的
接着回头看ViewModelProvider.get方法的实现:
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();//1
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);//2
}
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);//3
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
注释1处得到类的名称,对这个名称进行字符串拼接,作为注释2处方法的参数,DEFAULT_KEY的值为: “androidx.lifecycle.ViewModelProvider.DefaultKey”。
因此,注释3处的key值实际上就是”androidx.lifecycle.ViewModelProvider.DefaultKey”+类名。根据这个key值从ViewModelStore获取ViewModel(ViewModel的实现类)。如果ViewModel能转换为modelClass类的对象,直接返回该ViewModel。