其他相关文章:
android之LifeCycle:https://blog.youkuaiyun.com/li6472/article/details/119795952?spm=1001.2014.3001.5501
android之LiveData:https://blog.youkuaiyun.com/li6472/article/details/119784791?spm=1001.2014.3001.5501
android之MVVM框架使用之xml详解:https://blog.youkuaiyun.com/li6472/article/details/119782825?spm=1001.2014.3001.5501
android之MVVM框架使用之BaseObservable与双向绑定MVVM框架使用之BaseObservable与双向绑定:https://blog.youkuaiyun.com/li6472/article/details/119782825?spm=1001.2014.3001.5501
android之MVVM理解:https://blog.youkuaiyun.com/li6472/article/details/119762190?spm=1001.2014.3001.5501
amdroid之MVVM写一个简单的程序:https://blog.youkuaiyun.com/li6472/article/details/119761806?spm=1001.2014.3001.5501
一 主要应用场合
ViewModel主要是拿来获取或者保留Activity/Fragment所需要的数据的,开发者可以在Activity/Fragment中观察ViewModel中的数据更改(这里需要配合LiveData食用).
二 优点
ViewModel旨在以生命周期意识的方式存储和管理用户界面相关的数据,它可以用来管理Activity和Fragment中的数据.还可以拿来处理Fragment与Fragment之间的通信等等.
当Activity或者Fragment创建了关联的ViewModel,那么该Activity或Fragment只要处于活动状态,那么该ViewModel就不会被销毁,即使是该Activity屏幕旋转时重建了.所以也可以拿来做数据的暂存.
三 引入ViewModel
implementation "androidx.lifecycle:lifecycle-common-java8:2.2.0"
四 ViewModel基本使用
public class MyViewModel extends ViewModel {
private MutableLiveData<List<User>> users;
public LiveData<List<User>> getUsers() {
if (users == null) {
users = new MutableLiveData<List<Users>>();
loadUsers();
}
return users;
}
private void loadUsers() {
// 异步调用获取用户列表
}
}
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// 更新 UI
});
}
}
五 ViewModel生命周期
左侧表示Activity的生命周期状态,右侧绿色部分表示ViewModel的生命周期范围。当屏幕旋转的时候,Activity会被recreate,Activity会经过几个生命周期方法,但是这个时候ViewModel还是之前的对象,并没有被重新创建,只有当Activity的finish()方法被调用时,ViewModel.onCleared()方法会被调用,对象才会被销毁。这张图很好的描述了是当Activity被recreate时,ViewModel的生命周期。
另外,有个注意的地方:在ViewModel中不要持有Activity的引用。为什么要注意这一点呢?从上面的图我们看到,当Activity被recreate时,ViewModel对象并没有被销毁,如果Model持有Activity的引用时就可能会导致内存泄漏。那如果你要使用到Context对象怎么办呢,那就使用ViewModel的子类AndroidViewModel吧。
六 通信
得到ViweModel对象,首先是通过ViewModelProviders.of()方法得到ViewModelProvider对象,然后调用ViewModelProvider.get()方法得到ViewModel对象。
1 Fragment与Fragment"通信"
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中改变SharedViewModel中LiveData保存的数据
- 然后在DetailFragment中会收到数据改变的通知。这样一个过程就完成了MasterFragment和DetailFragment之间的一次简单的通信。
- SharedViewModel model =ViewModelProviders.of(getActivity()).get(SharedViewModel.class);ViewModelProviders.of()方法传入的是Activity对象,如果你改成Fragment对象DetailFragment里就只能傻等了,永远不会收到数据改变的通知。因为如果传给方法ViewModelProviders.of()的对象不同时,最终得到的就不是同一个ViewModel对象,这一点也可以通过打印两个Fragment中的SharedViewModel验证。
2 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中创建的是一个实例
UserModel userModel = ViewModelProviders.of(getActivity()).get(UserModel.class);
}
}
七 原理图
ViewModelProviders是ViewModel工具类,该类提供了通过Fragment和Activity得到ViewModel的方法,而具体实现又是有ViewModelProvider实现的。ViewModelProvider是实现ViewModel创建、获取的工具类。在ViewModelProvider中定义了一个创建ViewModel的接口类——Factory。ViewModelProvider中有个ViewModelStore对象,用于存储ViewModel对象。
ViewModelStore是存储ViewModel的类,具体实现是通过HashMap来保存ViewModle对象。
ViewModel是个抽象类,里面只定义了一个onCleared()方法,该方法在ViewModel不在被使用时调用。ViewModel有一个子类AndroidViewModel,这个类是便于要在ViewModel中使用Context对象,因为我们前面提到是不能在ViewModel中持有Activity的引用。
ViewModelStores是ViewModelStore的工厂方法类,它会关联HolderFragment,HolderFragment有个嵌套类——HolderFragmentManager。
八 几个方法
1 ViewModelProviders.of(this) 方法
@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
Factory是ViewModelProvider的一个内部接口,它的实现类是拿来构建ViewModel实例的.它里面只有一个方法,就是创建一个ViewModel.
Factory有2个实现类:一个是NewInstanceFactory, 一个是AndroidViewModelFactory .
1 NewInstanceFactory
NewInstanceFactory专门用来实例化那种构造方法里面没有参数的class,并且ViewModel里面是不带Context的,然后它是通过newInstance()去实例化的.
2 AndroidViewModelFactory
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()方法
@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,内容要比这个多一些,但是由于没有关系到它,这里就不提及了)
@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;
}
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实例就还在啊,也就避免了横竖屏切换时的数据丢失.
2 viewModelProvider.get(UserModel.class)
面我们来到那句构建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.
3. ViewModelStore
上面多个地方用到了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();
}
}
4. ViewModel.onCleared() 资源回收
既然ViewModel是生命周期感知的,那么何时应该清理ViewModel呢?
我们来到FragmentActivity的onDestroy()方法,发现它是在这里清理的.
/**
* Destroy all fragments.
*/
@Override
protected void onDestroy() {
super.onDestroy();
if (mViewModelStore != null && !isChangingConfigurations()) {
mViewModelStore.clear();
}
mFragments.dispatchDestroy();
}
5. 再看 ViewModel
public abstract class ViewModel {
/**
* 这个方法会在ViewModel即将被销毁时调用,可以在这里清理垃圾
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}
6. AndroidViewModel
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;
}
}
哈哈,后面时一些源码,可以选择的看