ViewModel旨在以生命周期意识的方式存储和管理用户界面相关的数据,它能够用来管理Activity和Fragment中的数据。还能够拿来处理Fragment与Fragment之间的通讯等等。android
当Activity或者Fragment建立了关联的ViewModel,那么该Activity或Fragment只要处于活动状态,那么该ViewModel就不会被销毁,即便是该Activity屏幕旋转时重建了。因此也能够拿来作数据的暂存。面试
ViewModel主要是拿来获取或者保留Activity/Fragment所须要的数据的,开发者能够在Activity/Fragment中观察ViewModel中的数据更改(这里须要配合LiveData食用)。小程序
ViewModel只是用来管理UI的数据的,千万不要让它持有View、Activity或者Fragment的引用(当心内存泄露)。缓存
本文以由浅入深的方式学习ViewModel。性能优化
//引入AndroidX吧,替换掉support包
implementation 'androidx.appcompat:appcompat:1.0.2'
def lifecycle_version = "2.0.0"
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
复制代码
简单使用起来bash
定义一个User数据类。网络
class User implements Serializable {
public int age;
public String name;
public User(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' + '}'; } } 复制代码
而后引出咱们今天的主角ViewModel。架构
public class UserModel extends ViewModel {
public final MutableLiveData<User> mUserLiveData = new MutableLiveData<>();
public UserModel() {
//模拟从网络加载用户信息
mUserLiveData.postValue(new User(1, "name1"));
}
//模拟 进行一些数据骚操做
public void doSomething() {
User user = mUserLiveData.getValue();
if (user != null) {
user.age = 15;
user.name = "name15";
mUserLiveData.setValue(user);
}
}
}
复制代码
这时候在Activity中就可使用ViewModel了。其实就是一句代码简单实例化,而后就可使用ViewModel了。app
//这些东西我是引入的androidx下面的
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
public class MainActivity extends FragmentActivity {
private TextView mContentTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContentTv = findViewById(R.id.tv_content);
//构建ViewModel实例
final UserModel userModel = ViewModelProviders.of(this).get(UserModel.class);
//让TextView观察ViewModel中数据的变化,并实时展现
userModel.mUserLiveData.observe(this, new Observer<User>() {
@Override
public void onChanged(User user) {
mContentTv.setText(user.toString());
}
});
findViewById(R.id.btn_test).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//点击按钮 更新User数据 观察TextView变化
userModel.doSomething();
}
});
}
}
复制代码
这个时候,咱们点击一下按钮(user中的age变为15),咱们能够旋转手机屏幕(这个时候其实Activity是从新建立了,也就是onCreate()方法被再次调用,可是ViewModel实际上是没有从新建立的,仍是以前那个ViewModel),可是当咱们旋转以后,发现TextView上显示的age竟然仍是15,这就是ViewModel的魔性所在。这个就不得不提ViewModel的生命周期了,它只有在Activity销毁以后,它才会自动销毁(因此别让ViewModel持有Activity引用啊,会内存泄露的)。 下面引用一下谷歌官方的图片,将ViewModel的生命周期展现的淋漓尽致。 框架
有了ViewModel,Activity与Fragment能够共享一个ViewModel,由于Fragment是依附在Activity上的,在实例化ViewModel时将该Activity传入ViewModelProviders,它会给你一个该Activity已建立好了的ViewModel,这个Fragment能够方便的访问该ViewModel中的数据。在Activity中修改userModel数据后,该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.
});
}
}
复制代码
二、而后在MasterFragment和DetailFragment均可以拿到该ViewModel,拿到了该ViewModel就能够拿到里面的数据了,至关于间接经过ViewModel通讯了。so easy…
咱们从下面这句代码start.
final UserModel userModel = ViewModelProviders.of(this).get(UserModel.class);
复制代码
咱们跟着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);
}
复制代码
ViewModelProviders里面的of()函数实际上是为了方便咱们构建一个ViewModelProvider。而ViewModelProvider,一看名字就知道干啥的了,就是提供ViewModel的。
Factory是ViewModelProvider的一个内部接口,它的实现类是拿来构建ViewModel实例的。它里面只有一个方法,就是建立一个ViewModel。
/**
* Implementations of {@code Factory} interface are responsible to instantiate ViewModels.
*/
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 {
@SuppressWarnings("ClassNewInstance")
@NonNull
@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);
}
}
}
复制代码
NewInstanceFactory专门用来实例化那种构造方法里面没有参数的class,而且ViewModel里面是不带Context的,而后它是经过newInstance()去实例化的。
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
private static AndroidViewModelFactory sInstance;
/**
* Retrieve a singleton instance of AndroidViewModelFactory.
*
* @param application an application to pass in {@link AndroidViewModel}
* @return A valid {@link AndroidViewModelFactory}
*/
@NonNull
public static AndroidViewModelFactory getInstance(@NonNull Application application) {
if (sInstance == null) {
sInstance = new AndroidViewModelFactory(application);
}
return sInstance;
}
private Application mApplication;
/**
* Creates a {@code AndroidViewModelFactory}
*
* @param application an application to pass in {@link AndroidViewModel}
*/
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的。
它是经过newInstance(application)去实例化的。若是有带application参数则是这样实例化。
若是没有带application参数的话,则仍是会走newInstance()方法去构建实例。
AndroidViewModelFactory经过构造方法给ViewModel带入Application,就能够在ViewModel里面拿到Context,由于Application是APP全局的,那么不存在内存泄露的问题,完美解决了有些ViewModel里面须要Context引用,可是又担忧内存泄露的问题。
下面咱们继续ViewModelProviders.of(this)方法继续分析吧,注意最后一句new ViewModelProvider(activity.getViewModelStore(), factory);第一个参数会调用activity的getViewModelStore()方法(这个方法会返回ViewModelStore,这个类是拿来存储ViewModel的,下面会说到),这里的activity是androidx.fragment.app.FragmentActivity,看一下这个getViewModelStore()方法。
/**
* 获取这个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;
}
复制代码
Android横竖屏切换时会触发onSaveInstanceState(),而还原时会调用onRestoreInstanceState(),可是Android的Activity类还有2个方法名为onRetainNonConfigurationInstance()和getLastNonConfigurationInstance()这两个方法。
来具体看看这2个素未谋面的方法。
/**
保留全部fragment的状态。你不能本身覆写它!若是要保留本身的状态,请使用onRetainCustomNonConfigurationInstance()
这个方法在FragmentActivity里面
*/
@Override
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();
if (fragments == null && mViewModelStore == null && custom == null) {
return null;
}
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = mViewModelStore;
nci.fragments = fragments;
return nci;
}
//这个方法在Activity里面,而mLastNonConfigurationInstances.activity实际就是就是上面方法中年的nci
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
复制代码
咱们来看看getLastNonConfigurationInstance()的调用时机,
protected void onCreate(@Nullable Bundle savedInstanceState) {
......
super.onCreate(savedInstanceState);
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null && nc.viewModelStore != null && mViewModelStore == null) {
mViewModelStore = nc.viewModelStore;
}
......
}
复制代码
没想到吧,Activity在横竖屏切换时悄悄保存了viewModelStore,放到了NonConfigurationInstances实例里面,横竖屏切换时保存了又恢复了回来,至关于ViewModel实例就还在啊,也就避免了横竖屏切换时的数据丢失。
下面咱们来到那句构建ViewModel代码的后半段,它是ViewModelProvider的get()方法,看看实现,其实很简单。
public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
}
return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//先取缓存 有缓存则用缓存
ViewModel viewModel = mViewModelStore.get(key);
if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
//无缓存 则从新经过mFactory构建
viewModel = mFactory.create(modelClass);
//缓存起来
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
复制代码
大致思路是利用一个key来缓存ViewModel,有缓存则用缓存的,没有则从新构建。构建时使用的factory是上面of()方法的那个factory。
上面多个地方用到了ViewModelStore,它其实就是一个普普统统的保存ViewModel的类。
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();
}
}
复制代码
ViewModelStore有一个HashMap专门用于存储,普通吧。
下面看看什么时候调用的clear()。
既然ViewModel是生命周期感知的,那么什么时候应该清理ViewModel呢?
咱们来到FragmentActivity的onDestroy()方法,发现它是在这里清理的。
/**
* Destroy all fragments.
*/
@Override
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
mFragments.dispatchDestroy();
}
复制代码
不少朋友可能就要问了,ViewModel究竟是什么?
public abstract class ViewModel {
/**
* 这个方法会在ViewModel即将被销毁时调用,能够在这里清理垃圾
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}
复制代码
其实很简单,就一个抽象类,里面就一个空方法??? 我擦,搞了半天,原来ViewModel不是主角…
ViewModel有一个子类,是AndroidViewModel.它里面有一个Application的属性,仅此而已,为了方便在ViewModel里面使用Context。
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;
public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}
/**
* Return the application.
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
@NonNull
public <T extends Application> T getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}
复制代码
ViewModel 的源码其实很少,理解起来比较容易,主要是官方FragmentActivity提供了技术实现,onRetainNonConfigurationInstance()保存状态,getLastNonConfigurationInstance()恢复。
原来Activity还有这么2个玩意儿,以前我还只是知道onSaveInstanceState()和onRestoreInstanceState(),涨姿式了。
想学习更多Android知识,或者获取相关资料请加入Android开发交流群:1018342383。 有面试资源系统整理分享,Java语言进阶和Kotlin语言与Android相关技术内核,APP开发框架知识, 360°Android App全方位性能优化。Android前沿技术,高级UI、Gradle、RxJava、小程序、Hybrid、 移动架构师专题项目实战环节、React Native、等技术教程!架构师课程、NDK模块开发、 Flutter等全方面的 Android高级实践技术讲解。还有在线答疑