Android架构组件——ViewModel
概述 ViewModel,从字面上理解的话,它肯定是跟视图(View)以及数据(Model)相关的。正像它字面意思一样,它是负责准备和管理和UI组件(Fragment/Activity)相关的数据类,也就是说ViewModel是用来管理UI相关的数据的,同时ViewModel还可以用来负责UI组件间的通信。
之前存在的问题 ViewModel用来存储和管理UI相关的数据,可于将一个Activity或Fragment组件相关的数据逻辑抽象出来,并能适配组件的生命周期,如当屏幕旋转Activity重建后,ViewModel中的数据依然有效。 引入ViewModel之前,存在如下几个问题: 通常Android系统来管理UI controllers(如Activity、Fragment)的生命周期,由系统响应用户交互或者重建组件,用户无法操控。当组件被销毁并重建后,原来组件相关的数据也会丢失,如果数据类型比较简单,同时数据量也不大,可以通过onSaveInstanceState()存储数据,组件重建之后通过onCreate(),从中读取Bundle恢复数据。但如果是大量数据,不方便序列化及反序列化,则上述方法将不适用。
UI controllers经常会发送很多异步请求,有可能会出现UI组件已销毁,而请求还未返回的情况,因此UI controllers需要做额外的工作以防止内存泄露。
当Activity因为配置变化而销毁重建时,一般数据会重新请求,其实这是一种浪费,最好就是能够保留上次的数据。
UI controllers其实只需要负责展示UI数据、响应用户交互和系统交互即可。但往往开发者会在Activity或Fragment中写许多数据请求和处理的工作,造成UI controllers类代码膨胀,也会导致单元测试难以进行。我们应该遵循职责分离原则,将数据相关的事情从UI controllers中分离出来。 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() {
// 异步调用获取用户列表
}
} 新的Activity如下: public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getUsers().observe(this, users -> {
// 更新 UI
});
}
} 如果Activity被重新创建了,它会收到被之前Activity创建的相同MyViewModel实例。当所属Activity终止后,框架调用ViewModel的onCleared()方法清除资源。 因为ViewModel在指定的Activity或Fragment实例外存活,它应该永远不能引用一个View,或持有任何包含Activity context引用的类。如果ViewModel需要Application的context(如获取系统服务),可以扩展AndroidViewmodel,并拥有一个构造器接收Application。 在Fragment间共享数据 一个Activity中的多个Fragment相互通讯是很常见的。之前每个Fragment需要定义接口描述,所属Activity将二者捆绑在一起。此外,每个Fragment必须处理其他Fragment未创建或不可见的情况。通过使用ViewModel可以解决这个痛点,这些Fragment可以使用它们的Activity共享ViewModel来处理通讯: 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 onActivityCreated() {
model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
} public class DetailFragment extends LifecycleFragment {
public void onActivityCreated() {
SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
model.getSelected().observe(this, { item ->
// update UI
});
}
} 注意:上面两个Fragment都用到了如下代码来获取ViewModel,getActivity()返回的是同一个宿主Activity,因此两个Fragment之间返回的是同一个SharedViewModel对象。 SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class); 1 这种方式的好处包括: Activity不需要做任何事情,也不需要知道通讯的事情
Fragment不需要知道彼此,除了SharedViewModel进行联系。如果它们(Fragment)其中一个消失了,其余的仍然能够像往常一样工作。
每个Fragment有自己的生命周期,而且不会受其它Fragment生命周期的影响。事实上,一个Fragment替换另一个Fragment,UI的工作也不会受到任何影响。 ViewModel的生命周期 ViewModel对象的范围由获取ViewModel时传递至ViewModelProvider的Lifecycle所决定。ViewModel始终处在内存中,直到Lifecycle永久地离开—对于Activity来说,是当它终止(finish)的时候,对于Fragment来说,是当它分离(detached)的时候。
这里写图片描述
上图左侧为Activity的生命周期过程,期间有一个旋转屏幕的操作;右侧则为ViewModel的生命周期过程。 一般通过如下代码初始化ViewModel: viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class); 1 this参数一般为Activity或Fragment,因此ViewModelProvider可以获取组件的生命周期。 Activity在生命周期中可能会触发多次onCreate(),而ViewModel则只会在第一次onCreate()时创建,然后直到最后Activity销毁。
ViewModel相关类图 借用Android架构组件(三)——ViewModel的类图:
这里写图片描述 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。 ViewModel相关时序图 追溯创建一个ViewModel的源码,会察觉需要的步骤有点多。下面以在Fragment中得到ViewModel对象为例看下整个过程的时序图。
借用Android架构组件(三)——ViewModel的时序图:
这里写图片描述 时序图看起来比较复杂,但是它只描述了两个过程: 得到ViewModel对象。
HolderFragment被销毁时,ViewModel收到onCleared()通知。 ViewModel相关源码分析 ViewModelProviders类的具体实现: public class ViewModelProviders { private static Application checkApplication(Activity activity) {
Application application = activity.getApplication();
if (application == null) {
throw new IllegalStateException("Your activity/fragment is not yet attached to "
+ "Application. You can't request ViewModel before onCreate call.");
}
return application;
} private static Activity checkActivity(Fragment fragment) {
Activity activity = fragment.getActivity();
if (activity == null) {
throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
}
return activity;
} @NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
ViewModelProvider.AndroidViewModelFactory factory =
ViewModelProvider.AndroidViewModelFactory.getInstance(
checkApplication(checkActivity(fragment)));
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
} @NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
ViewModelProvider.AndroidViewModelFactory factory =
ViewModelProvider.AndroidViewModelFactory.getInstance(
checkApplication(activity));
return new ViewModelProvider(ViewModelStores.of(activity), factory);
} @NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory) {
checkApplication(checkActivity(fragment));
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
} @NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@NonNull Factory factory) {
checkApplication(activity);
return new ViewModelProvider(ViewModelStores.of(activity), factory);
} ViewModelProviders提供了四个of()方法,四个方法功能类似,其中of(FragmentActivity activity, Factory factory)和of(Fragment fragment, Factory factory)提供了自定义创建ViewModel的方法。
1. 判断Fragment的是否Attached to Activity,Activity的Application对象是否为空。
2. 创建ViewModel对象看似很简单,一行代码搞定。 new ViewModelProvider(ViewModelStores.of(fragment), factory) 先看看ViewModelStores.of()方法: @NonNull
@MainThread
public static ViewModelStore of(@NonNull Fragment fragment) {
if (fragment instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) fragment).getViewModelStore();
}
return holderFragmentFor(fragment).getViewModelStore();
} 继续深入发现其实是实现了一个接口: public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
} holderFragmentFor()是HolderFragment的静态方法,HolderFragment继承自Fragment。我们先看holderFragment()方法的具体实现 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(Fragment fragment) {
return sHolderFragmentManager.holderFragmentFor(fragment);
} @NonNull
@Override
public ViewModelStore getViewModelStore() {
return mViewModelStore;
} 继续看HolderFragmentManager.holderFragmentFor()方法的具体实现 HolderFragment holderFragmentFor(Fragment parentFragment) {
FragmentManager fm = parentFragment.getChildFragmentManager();
HolderFragment holder = findHolderFragment(fm);
if (holder != null) {
return holder;
}
holder = mNotCommittedFragmentHolders.get(parentFragment);
if (holder != null) {
return holder;
} parentFragment.getFragmentManager()
.registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
holder = createHolderFragment(fm);
mNotCommittedFragmentHolders.put(parentFragment, holder);
return holder;
} private FragmentLifecycleCallbacks mParentDestroyedCallback =
new FragmentLifecycleCallbacks() {
@Override
public void onFragmentDestroyed(FragmentManager fm, Fragment parentFragment) {
super.onFragmentDestroyed(fm, parentFragment);
HolderFragment fragment = mNotCommittedFragmentHolders.remove(
parentFragment);
if (fragment != null) {
Log.e(LOG_TAG, "Failed to save a ViewModel for " + parentFragment);
}
}
}; private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
HolderFragment holder = new HolderFragment(); // 创建HolderFragment对象
fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
return holder;
} public HolderFragment() {
//这个是关键,这就使得Activity被recreate时,Fragment的onDestroy()和onCreate()不会被调用
setRetainInstance(true);
} setRetainInstance(boolean) 是Fragment中的一个方法。将这个方法设置为true就可以使当前Fragment在Activity重建时存活下来, 如果不设置或者设置为 false, 当前 Fragment 会在 Activity 重建时同样发生重建, 以至于被新建的对象所替代。 在setRetainInstance(boolean)为true的 Fragment 中放一个专门用于存储ViewModel的Map, 自然Map中所有的ViewModel都会幸免于Activity重建,让Activity, Fragment都绑定一个这样的Fragment, 将ViewModel存放到这个 Fragment 的 Map 中, ViewModel 组件就这样实现了。 到此为止,我们已经得到了ViewStore对象,前面我们在创建ViewModelProvider对象是通过这行代码实现的new ViewModelProvider(ViewModelStores.of(fragment), sDefaultFactory)现在再看下ViewModelProvider的构造方法 public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
this.mViewModelStore = store;
} 现在就可以通过ViewModelProvider.get()方法得到ViewModel对象,继续看下该方法的具体实现 @NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key); //从缓存中查找是否有已有ViewModel对象。 if (modelClass.isInstance(viewModel)) {
//noinspection unchecked
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
} viewModel = mFactory.create(modelClass); //创建ViewModel对象,然后缓存起来。
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
} ViewModelProvider.get()方法比较简单,注释中都写明了。最后我们看下ViewModelStore类的具体实现 public class ViewModelStore { private final HashMap<String, ViewModel> mMap = new HashMap<>(); final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.get(key);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
mMap.put(key, viewModel);
} final ViewModel get(String key) {
return mMap.get(key);
} public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
} ViewModelStore是缓存ViewModel的类,put()、get()方法用于存取ViewModel对象,另外提供了clear()方法用于清空缓存的ViewModel对象,在该方法中会调用ViewModel.onCleared()方法通知ViewModel对象不再被使用。 ViewModel收到onCleared()通知
HolderFragment的onDestroy()方法 @Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
} 在onDestroy()方法中调用了ViewModelStore.clear()方法,我们知道在该方法中会调用ViewModel的onCleared()方法。在你看了HolderFragment源码后,或许你会有个疑问,mViewModelStore保存的ViewModel对象是在哪里添加的呢? 细心的话,你会发现在ViewModelProvider的构造方法中,已经将HolderFragment中的ViwModelStore对象mViewModelStore的引用传递给了ViewModelProvider中的mViewModelStore,而在ViewModelProvider.get()方法中会向mViewModelStore添加ViewModel对象。
总结 ViewModel职责是为Activity或Fragment管理、请求数据,具体数据请求逻辑不应该写在ViewModel中,否则ViewModel的职责会变得太重,此处需要一个引入一个Repository,负责数据请求相关工作。具体请参考 Android架构组件。
ViewModel可以用于Activity内不同Fragment的交互,也可以用作Fragment之间一种解耦方式。
ViewModel也可以负责处理部分Activity/Fragment与应用其他模块的交互。
ViewModel生命周期(以Activity为例)起始于Activity第一次onCreate(),结束于Activity最终finish时。 官方:
https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html
————————————————
版权声明:本文为CSDN博主「Double-Smile」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_24442769/article/details/79426609
Android架构组件——ViewModel的更多相关文章
- 改造 Android 官方架构组件 ViewModel
前言 Android 官方架构组件在今年 5 月份 Google I/O 大会上被公布, 直到 11 月份一直都是测试版, 由于工作比较繁忙, 期间我只是看过类似的文章, 但没有在实际项目中使用过, ...
- Android Jetpack组件 - ViewModel,LiveData使用以及原理
本文涉及的源码版本如下: com.android.support:appcompat-v7:27.1.1 android.arch.lifecycle:extensions:1.1.1 android ...
- Android 架构组件-Lifecycle、LiveData、ViewModel
Lifecycle Lifecycle组件包括LifecycleOwner.LifecleObserver,能方便监听Activity或者Fragment的生命周期. 步骤: 1.实现Lifecycl ...
- Android 架构组件 Room 介绍及使用
关于Room Room是Google官方提供的数据库ORM框架,使用起来非常方便.Room在SQLite上提供了一个抽象层,以便在利用SQLite的全部功能的同时能更加流畅的访问数据库. Room中三 ...
- Android官方架构组件介绍之LifeCycle
Google 2017 I/O开发者大会于近日召开,在开发者大会上谷歌除了发布了Android O等一些新产品之外,也对Android代码的架构做出了一个官方的回应. Google 2017 I/O开 ...
- Jetpack 架构组件 LiveData ViewModel MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- Android官方架构组件指南
此指南适用于那些曾经或现在进行Android应用的基础开发,并希望了解和学习编写Android程序的最佳实践和架构.通过学习来构建强大的生产级别的应用. 注意:此指南默认你对Android开发有比较深 ...
- Android官方架构组件介绍之LifeCycle(一)
Android官方架构组件介绍之LifeCycle 下面是官方提供的Android App开发的架构图: 从上图可以看到一些关键字:ViewModel,LiveData,Room等.其实看了上面视频的 ...
- android物理动画、Kotlin客户端、架构组件、菜单效果、应用选择器等源码
Android精选源码 Android一个有趣的Android动画交互设计 android可伸缩日历效果源码 关于界面,全新的卡片风格,支持夜晚模式 Android 用 Kotlin 实现的基于物理的 ...
随机推荐
- vi中使用删除键(backspace)只能删除到行首不能跳到上一行怎么处理?
答: 在~/.vimrc中加入以下内容: set backspace=2
- [转]图片Premultiplied Alpha到底是干嘛用的
Premultiplied Alpha 这个概念做游戏开发的人都不会不知道.Xcode 的工程选项里有一项 Compress PNG Files,会对 PNG 进行 Premultiplied Alp ...
- 数据分析入门——pandas之DataFrame数据丢失
一.数据丢失分类 1)nd中分为两种:None和np.nan(NaN) 其中,None是python中的对象,是一个object:而nan是一个float类型 两种不同的类型,运算速度也是不同的 2) ...
- matlab学习笔记4--多媒体文件的保存和读取
一起来学matlab-matlab学习笔记4 数据导入和导出_2 多媒体文件的保存和读取 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考书籍 <matlab 程序设计与综合应用&g ...
- Java之安装JDK
因为Java程序必须运行在JVM只是,所以我们第一件事情就是安装JDK 从Oracle官网下载最新稳定版JDK 一,Linux系统CentOS安装JDK 下载rpm安装包 安装 rpm -ivh jd ...
- oracle 常用命令【创建表空间用户及删除】
/*第1步:创建临时表空间 */ create temporary tablespace ZJY_TEMP tempfile '/usr/u01/app/oradata/orcl/ZJY_TEMP.d ...
- 倒数第K个结点
typedef struct Lnode{ int data; struct Lnode *next; }Lnode,*Link; Link fi(Link head,int k){ Link fa ...
- 使用SnowFlake算法生成唯一ID
转自:https://segmentfault.com/a/1190000007769660 考虑过的方法有 直接用时间戳,或者以此衍生的一系列方法 Mysql自带的uuid 以上两种方法都可以查到就 ...
- javaNIO核心概念
在java的阻塞IO中使用InputStream和outputStream来进行输入和输出,那么两种流是相互独立使用的,而且每次数据传输都要通过“用户态数据”向“os内核态数据”copy或从“os内核 ...
- 面试官问你MySQL的优化,看这篇文章就够了
作者:zhangqh segmentfault.com/a/1190000012155267 一.EXPLAIN 做MySQL优化,我们要善用 EXPLAIN 查看SQL执行计划. 下面来个简单的示例 ...