在本文中,我们将给您介绍关于Android架构组件JetPack之ViewModel(二)的详细内容,并且为您解答androidjetpackmvvm框架搭建的相关问题,此外,我们还将为您提供关于An
在本文中,我们将给您介绍关于Android架构组件JetPack之ViewModel(二)的详细内容,并且为您解答android jetpack mvvm框架搭建的相关问题,此外,我们还将为您提供关于Android Jetpack -- ViewModel篇(一)、Android Jetpack 架构组件 (一) 与 AndroidX、Android Jetpack-ViewModel、android Jetpack—ViewModel使用方法和详细原理解析的知识。
本文目录一览:- Android架构组件JetPack之ViewModel(二)(android jetpack mvvm框架搭建)
- Android Jetpack -- ViewModel篇(一)
- Android Jetpack 架构组件 (一) 与 AndroidX
- Android Jetpack-ViewModel
- android Jetpack—ViewModel使用方法和详细原理解析
Android架构组件JetPack之ViewModel(二)(android jetpack mvvm框架搭建)
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680
概述
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不需要做任何事情,也不需要知道通讯的事情
2.Fragment不需要知道彼此,除了Sharedviewmodel进行联系。如果它们(Fragment)其中一个消失了,其余的仍然能够像往常一样工作。
3.每个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);
this参数一般为Activity或Fragment,因此viewmodelProvider可以获取组件的生命周期。
Activity在生命周期中可能会触发多次onCreate(),而viewmodel则只会在第一次onCreate()时创建,然后直到最后Activity销毁。
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对象为例看下整个过程的时序图。

时序图看起来比较复杂,但是它只描述了两个过程:
得到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的方法。
- 判断Fragment的是否Attached to Activity,Activity的Application对象是否为空。
- 创建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://blog.csdn.net/qq_24442769/article/details/79426609
阿里P7移动互联网架构师进阶视频(每日更新中)免费学习请点击:https://space.bilibili.com/474380680
Android Jetpack -- ViewModel篇(一)
ViewModel的出现主要为了解决两个问题:
1.当Actvitiy销毁重建过程中的数据恢复问题,虽然原来可以使用onSaveInstanceState()来完成,但是只支持能被序列化的数据而且是小量数据,对于大量数据则显得有点无力。
2.UI控制器的工作繁忙,UI控制器主要用于处理显示,交互,其他的额外操作可以委托给其他类完成,将不应该分配给UI的任务分离出来是必要的,这也就是上面所说的分离关注点原则。
下面是示意图
ViewModel实例
ViewModel在配置更改期间能自动保留其对象,以便它们所持有的数据可立即用于下一个 Activity 或片段 Fragment
具体的实例我在以前的一篇博客中讲过了,想要了解请点击 Android学习进度二 进行查看。
回到最上面的那个图,图说明了ViewModel的作用域涉及到整个生命周期,当获取ViewModel时,ViewModel的生命周期限定为传入ViewModelProvider的对象的生命周期。也就是对于以下场景(引用官方示例)
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.
});
}
}
由于传入的是Activity,所以其作用域为整个Activity,不同的Fragment可以通过ViewModelProviders获取到同一个ViewModel,这样有以下的好处:
- Activity无须参与Fragment之间的交互。Activity与Fragment无关
- Fragment之间也无需互相建立联系,Fragment与Fragment无关
- 每个Fragment都有自己的生命周期,即使被替换也不会有任何影响
ViewModel加强,支持异常生命周期
有些时候在Activity被意外杀死,如清理后台等会直接跳过onDestory()而是回调onSaveInstanceState()异常杀死下的生命周期,这个时候ViewModel也会被杀死,再次恢复的时候便会被重建,这样,原来的数据也就丢失了,因此我们需要改进一下ViewModel以支持异常退出情况下的重建。
首先很容易是想到通过onSaveInstanceState() 来保存,然后通过SaveInstanceState 来恢复,虽然也是一种可行的方法,但是跟ViewModel没什么关联,ViewModel也提供了类似SavedInstanceState的方法。
SavedStateHandle :用于保存状态的数据类型,是一个key-value的map,类似于Bundle。
具体用法
public class ViewModelWithData extends ViewModel {
private MutableLiveData<Integer> number;
private SavedStateHandle handle;
private static final String KEY_NUMBER = "number";
public ViewModelWithData(SavedStateHandle handle) {
this.handle = handle;
number = new MutableLiveData<>();
number.setValue(0);
}
public MutableLiveData<Integer> getNumber() {
if (!handle.contains(KEY_NUMBER)) {
handle.set(KEY_NUMBER, 0);
}
return handle.getLiveData(KEY_NUMBER);
}
public void addNumber(int n) {
getNumber().setValue(getNumber().getValue() + n);
}
}
public class LiveDataActivity extends AppCompatActivity {
private ViewModelWithData viewModelWithData;
ActivityLiveDataBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_live_data);
viewModelWithData = ViewModelProviders.of(this, new SavedStateVMFactory(this)).get(ViewModelWithData.class);
binding.setData(viewModelWithData);
binding.setLifecycleOwner(this);
}
}
Android Jetpack 架构组件 (一) 与 AndroidX
一、 概述
自 2008 年 9 月 22 日谷歌发布 Android 1.0 版本到前不久 Android 12 版本到发布,Android 已经陪伴我们走过了 12 个年头。可以说,经过 12 年的打磨和沉淀,Android 的技术体系已经非常的成熟了。比如说,一开始时框架很少,也没有什么规范,所有的代码都是要自己写,但是现在,我们很少会关系这种基础代码,因为一些框架和工具的出现正在帮助开发者完成这方面的工作。
不过,虽然我们可以使用这些框架和技术来达到快速迭代的目的,但是越来越杂的技术选型也让 Android 开发者无从选择,最终导致做出来的应用质量参差不齐。长久以来,Android 一直没有制定一个规范来姐姐这一问题,只要能够实现功能,代码怎么编写都是可以的。虽然 Android 官方没有推出开发标准,但是一些技术社区出于更高效的进行协同开发,逐渐引入了 MVP 和 MVVM 等应用开发架构,使用这些框架开发出来的应用,无论从项目质量还是代码的可读性和可维护性来说,都更加出色,于是这些框架和技术逐渐流行起来。
这些情况被谷歌发现后,最终在 Goole I/O 2018 大会上推出了全新的 Android Jetpack 应用开发架构。或许称 Android Jetpack 为一个架构有点不准确,更多的地方将它称为 Android 应用开发的工具集,Jetpack 是一套库、工具和指南的集合,旨在帮助开发者更轻松地编写优质应用。事实上,Android Jetpack 所包含的内容是比较庞大的,主要由基础组件、架构组件、行为组件和 UI 组件构成,如下图所示。
并且,
本文同步分享在 博客 “xiangzhihong8”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与 “OSC 源创计划”,欢迎正在阅读的你也加入,一起分享。
Android Jetpack-ViewModel
一、概述
为什么需要viewmodel或者说viewmodel的优势是什么?
如果Activity或者Fragment销毁或者重建,存储在其中的数据会丢失,对于简单的数据比如Activity可以使用onSaveInstanceState()
方法来从onCreate()
中恢复数据,但这个方法只适合可以序列化再反序列化的少量数据,而不适合较大的数据。
另外一个问题是,界面经常需要异步操作,比如网络请求等,当界面销毁时,往往需要手动维护异步取消的动作,这无疑显得特别繁琐。并且把所有代码都写在界面中,会变得特别臃肿。
于是就需要将视图数据与界面分离,让层次清晰且高效。且viewmodel可以维护自己的生命周期,不需要手动操作,这无疑大大降低开发难度。
二、简单实现viewmodel
简单的viewmodel实现
class UserInfoviewmodel : viewmodel() {
//延迟创建LiveData对象
val userInfoLiveData: mutablelivedata<UserInfo> by lazy {
//创建LiveData
mutablelivedata<UserInfo>()
}
/**
* 请求用户信息
*/
fun requestUserInfo() {
//伪代码:请求到了用户信息
var userInfo = UserInfo("zhangsan", 18)
//将请求到的值userInfo设置给LiveData,更新数据
userInfoLiveData.value = userInfo
}
}
在Activity中调用
class MainActivity : AppCompatActivity() {
//创建viewmodel对象
private val userInfoviewmodel: UserInfoviewmodel by lazy {
UserInfoviewmodel()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var textview = findViewById<TextView>(R.id.tv_start)
//调用viewmodel的方法请求数据
userInfoviewmodel.requestUserInfo()
//通知修改数据
userInfoviewmodel.userInfoLiveData.observe(this, Observer<UserInfo> {
//拿到了请求到的UserInfo对象,通知修改数据
textview.text = it?.name
})
}
}
需要注意的点:
- 如果Activity重新创建,它接收的viewmodel实例与第一个Activity创建的实例相同。
- 当Activity完成(不能简单理解为destroy)时,框架会调用
viewmodel
的onCleared()
方法,以便它可以清理资源。 - viewmodel的生命周期比视图的生命周期长,所以viewmodel绝不能持有视图、Lifecycle或Activity的上下文等引用,否则就会造成内存泄漏。
- viewmodel绝不能观察对生命周期感知型对象(如LiveData对象)的更改。
- 如果viewmodel需要持有上下文,只能是
Application
的上下文,扩展Androidviewmodel
类并通过构造方法传入,如下代码:
/**
* 继承Androidviewmodel,创建时需要通过构造方法传入Application对象
*/
class CustomApplicationviewmodel(application: Application) : Androidviewmodel(application) {
override fun onCleared() {
super.onCleared()
}
}
//因为这个viewmodel持有Application的引用,所以它的生命周期会很长
//于是可以在Application类中创建全局的viewmodel
class App : Application() {
//创建全局viewmodel对象
val mCustomApplicationviewmodel: CustomApplicationviewmodel by lazy {
CustomApplicationviewmodel(this)
}
override fun onCreate() {
super.onCreate()
}
}
//调用
var app: App = application as App
app.mCustomApplicationviewmodel...
三、viewmodel的初始化方式
1、懒加载的初始化方式
这种方式是最简单的方式,但是封装架构的时候一般不这样用。代码见本文第二章节最上面viewmodel的代码。
2、简单工厂初始化viewmodel
viewmodel的类
class UserInfoviewmodel : viewmodel() {
...
}
创建viewmodel对象
var userInfoviewmodel = viewmodelProvider.NewInstanceFactory()
.create(UserInfoviewmodel::class.java)
或另一种写法
var userInfoviewmodel =
viewmodelProvider(this).get(UserInfoviewmodel::class.java)
二者的区别create是创建一个新的实例,而get是先从HashMap中找,找不到就创建新的实例。看下源码也就知道为什么重新创建的viewmodel是同一个对象。
public <T extends viewmodel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
//viewmodelStore底层是HashMap集合
//通过key从集合中取出viewmodel对象
viewmodel viewmodel = mviewmodelStore.get(key);
//判断这个viewmodel类的实例对象是否是需要viewmodel类的实例对象
if (modelClass.isinstance(viewmodel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewmodel);
}
//如果是直接返回
return (T) viewmodel;
} else {
//noinspection StatementWithEmptyBody
if (viewmodel != null) {
// Todo: log a warning.
}
}
//如果不存在就创建
if (mFactory instanceof KeyedFactory) {
viewmodel = ((KeyedFactory) mFactory).create(key, modelClass);
} else {
viewmodel = mFactory.create(modelClass);
}
//并且将创建的viewmodel实例对象存在HashMap中
mviewmodelStore.put(key, viewmodel);
return (T) viewmodel;
}
3、使用ktx扩展(推荐)
添加依赖
implementation 'androidx.activity:activity-ktx:1.4.0'
implementation 'androidx.fragment:fragment-ktx:1.4.1'
viewmodel的类
class UserInfoviewmodel : viewmodel() {
...
}
在Activity中创建viewmodel的方式
class MainActivity :AppCompatActivity() {
//通过ktx扩展创建viewmodel对象
private val userInfoviewmodel by viewmodels<UserInfoviewmodel>{
viewmodelProvider.NewInstanceFactory()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var textview = findViewById<TextView>(R.id.tv_start)
//调用viewmodel的方法请求数据
userInfoviewmodel.requestUserInfo()
//通知修改数据
userInfoviewmodel.userInfoLiveData.observe(this, Observer<UserInfo> {
//拿到了请求到的UserInfo对象,通知修改数据
textview.text = it?.name
})
}
}
在Fragment中创建viewmodel的方式
class CustomFragment:Fragment() {
//通过ktx扩展创建viewmodel对象
private val userInfoviewmodel by activityviewmodels<UserInfoviewmodel>{
viewmodelProvider.NewInstanceFactory()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
}
四、viewmodel的生命周期
viewmodel
对象存在的时间范围是获取 viewmodel
时传递给 viewmodelProvider
的 Lifecycle
。viewmodel
将一直留在内存中,直到限定其存在时间范围的 Lifecycle
永久消失:对于 activity,是在 activity 完成时;而对于 fragment,是在 fragment 分离时。
如下官方给的生命周期示例图:
上图说明了 Activity 经历屏幕旋转而后结束时所处的各种生命周期状态。该图还在 Activity 生命周期的旁边显示了 viewmodel
的生命周期。此图表说明了 Activity 的各种状态。这些基本状态同样适用于 Fragment 的生命周期。所以不要简单理解Activity的完成状态就是Destroy状态,上图屏幕旋转会经历一次onDestroy
。
通过上图可以看到,viewmodel创建后,它并不关心onDestroy之前的生命周期,它只关系视图控件(Activity,Fragment等)何时退出,我们跟踪一下源码。
1、Activity中viewmodel何时退出
首先通过上面的分析,可以知道viewmodel创建时会放入一个HashMap集合(viewmodelStore.mMap
)中,调用的时候也是从这个集合中取,那么何时从这个集合中删除?
//ComponentActivity部分源码
//ComponentActivity的构造方法,创建Activity时调用
public ComponentActivity() {
...
getLifecycle().addobserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//当收到Activity的ON_DESTROY事件时
if (event == Lifecycle.Event.ON_DESTROY) {
// Clear out the available context
mContextAwareHelper.clearavailableContext();
// And clear the viewmodelStore
//isChangingConfigurations()永远返回false
if (!isChangingConfigurations()) {
//清除viewmodelStore
getviewmodelStore().clear();
}
}
}
});
getLifecycle().addobserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
//担保viewmodelStore,如果不是正常的onDestroy这里会恢复viewmodelStore
ensureviewmodelStore();
getLifecycle().removeObserver(this);
}
});
...
}
可以看到当触发onDestroy事件时,viewmodel的集合确实会被清除掉,但是下面又有担保的方式恢复,看下担保恢复的方法:
@SuppressWarnings("WeakerAccess") /* synthetic access */
void ensureviewmodelStore() {
if (mviewmodelStore == null) {
//取出之前配置的实例
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
//恢复viewmodelStore
// Restore the viewmodelStore from NonConfigurationInstances
mviewmodelStore = nc.viewmodelStore;
}
//如果viewmodel还是null,创建新的viewmodelStore给这个新的Activity用
if (mviewmodelStore == null) {
mviewmodelStore = new viewmodelStore();
}
}
}
可以看到配置的实例中如果还有就取出来重新用,如果没有就创建一个新的viewmodelStore给新的Activity用。继续跟源码,跟到Activity类就是系统方法了,并没有找到答案。
@Nullable
public Object getLastNonConfigurationInstance() {
//如果不为null返回一个Activity实例
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
//给上面的Activity实例赋值的地方
...
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
...
/**
* Called by the system,as part of destroying an
* activity due to a configuration change,...
*/
public Object onRetainNonConfigurationInstance() {
return null; //系统调用的方法,只能看到null
}
2、Fragment中viewmodel何时退出
与上面的原理类似,看viewmodelStore何时移除
//FragmentStateManager
//销毁时的方法
void destroy() {
...
//是否Fragment应该被移除
boolean beingRemoved = mFragment.mRemoving && !mFragment.isInBackStack();
// Clear any prevIoUs saved state
if (beingRemoved && !mFragment.mBeingSaved) {
mFragmentStore.setSavedState(mFragment.mWho, null);
}
//Fragment是否应该被销毁
boolean shouldDestroy = beingRemoved
|| mFragmentStore.getNonConfig().shouldDestroy(mFragment);
//如果应该被销毁
if (shouldDestroy) {
FragmentHostCallback<?> host = mFragment.mHost;
boolean shouldClear;
if (host instanceof viewmodelStoreOwner) {
shouldClear = mFragmentStore.getNonConfig().isCleared();
} else if (host.getContext() instanceof Activity) {
Activity activity = (Activity) host.getContext();
shouldClear = !activity.isChangingConfigurations();
} else {
shouldClear = true;
}
if ((beingRemoved && !mFragment.mBeingSaved) || shouldClear) {
//移除的关键代码
mFragmentStore.getNonConfig().clearNonConfigState(mFragment);
}
...
}
void clearNonConfigState(@NonNull Fragment f) {
if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
Log.d(TAG, "Clearing non-config state for " + f);
}
clearNonConfigStateInternal(f.mWho);
}
void clearNonConfigState(@NonNull String who) {
if (FragmentManager.isLoggingEnabled(Log.DEBUG)) {
Log.d(TAG, "Clearing non-config state for saved state of Fragment " + who);
}
clearNonConfigStateInternal(who);
}
private void clearNonConfigStateInternal(@NonNull String who) {
// Clear and remove the Fragment's child non config state
FragmentManagerviewmodel childNonConfig = mChildNonConfigs.get(who);
if (childNonConfig != null) {
childNonConfig.onCleared();
mChildNonConfigs.remove(who);
}
// Clear and remove the Fragment's viewmodelStore
viewmodelStore viewmodelStore = mviewmodelStores.get(who);
if (viewmodelStore != null) {
//移除viewmodelStore
viewmodelStore.clear();
mviewmodelStores.remove(who);
}
}
五、在Fragment之间共享数据
Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的现象。多个 fragment 可以在其 activity 范围内共享 viewmodel
存储的数据。 先看下官方的示例代码,这里做了精简:
class Sharedviewmodel : viewmodel() {
//创建LiveData
val selected = mutablelivedata<Item>()
//为LiveData赋值
fun select(item: Item) {
selected.value = item
}
}
class ListFragment : Fragment() {
//创建(获取)viewmodel对象
private val model: Sharedviewmodel by activityviewmodels()
...
}
class DetailFragment : Fragment() {
//创建(获取)viewmodel对象
private val model: Sharedviewmodel by activityviewmodels()
...
}
请注意,这两个 Fragment 都会检索包含它们的 Activity。这样,当这两个 Fragment 各自获取 viewmodelProvider
时,它们会收到相同的 Sharedviewmodel
实例(其范围限定为该 Activity)。
此方法具有以下优势:
- Activity 不需要执行任何操作,也不需要对此通信有任何了解。
- 除了
Sharedviewmodel
约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。 - 每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。
简单看下源码是如何实现的,Fragment在Attach时会调用FragmentManager的下面的方法:
@SuppressWarnings("deprecation")
@SuppressLint("SyntheticAccessor")
void attachController(@NonNull FragmentHostCallback<?> host,
@NonNull FragmentContainer container, @Nullable final Fragment parent) {
...
// Get the FragmentManagerviewmodel
if (parent != null) {
mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
} else if (host instanceof viewmodelStoreOwner) {
//获取viewmodelStore赋值给FragmentManager自己的viewmodelStore
viewmodelStore viewmodelStore = ((viewmodelStoreOwner) host).getviewmodelStore();
mNonConfig = FragmentManagerviewmodel.getInstance(viewmodelStore);
} else {
mNonConfig = new FragmentManagerviewmodel(false);
}
...
getviewmodelStore()方法获取的就是Activity的viewmodelStore
@NonNull
@Override
public viewmodelStore getviewmodelStore() {
return FragmentActivity.this.getviewmodelStore();
}
android Jetpack—ViewModel使用方法和详细原理解析
关注我的公众号 “安安安安卓” 免费学知识
大部分人更关心用法,所以我先讲用法, 再讲对 viewmodel 的理解,最后讲源码
1、ViewModel 初始化方式
来到 androidx,ViewModel 的创建方式与老版本有了很大的不同,所以这里还是要将 Viewmodel 的初始化讲一下
1.1、安卓工厂初始化
每次都会重新创建 model,并且不受 ViewModelStore 管控,所以无特殊需求禁止使用该种方式
使用 AndroidViewModelFactory 工厂创建
- viewmodel 类定义
class AndroidFactoryViewModel(app:Application): AndroidViewModel(app) {
fun print(){
logEE("使用安卓默认工厂创建viewmodel")
}
}
- 创建 viewmodel 实例代码
val model = ViewModelProvider.AndroidViewModelFactory.getInstance(application)
.create(AndroidFactoryViewModel::class.java)
model.print()
1.2、简单工厂初始化 ViewModel
每次都会重新创建 model,并且不受 ViewModelStore 管控,所以无特殊需求禁止使用该种方式
NewInstanceFactory
- viewmodel 类定义代码
class SimpleFactoryViewModel: ViewModel() {
fun print(){
logEE("使用简单工厂创建viewmodel")
}
}
- 创建 viewmodel 代码
val model =ViewModelProvider.NewInstanceFactory().create(SimpleFactoryViewModel::class.java)
model.print()
1.3、自定义安卓工厂初始化
多次创建可以复用 model,不会重新创建
默认的工厂只能创建带 Application 的 ViewModel 实例的,我们通过自定义工厂实现自定义构造参数的目的
- 定义安卓工厂
class CustomAndroidViewModelFactory(val app: Application, private val data: String) :
ViewModelProvider.AndroidViewModelFactory(app) {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
return try {
modelClass.getConstructor(Application::class.java, String::class.java)
.newInstance(app, data)
} ...
}
return super.create(modelClass)
}
}
省略了 catch 中的代码,您可以到源码中查看
在我们的自定义工厂的构造中传入一个自定义的 data 参数,代表我们的自定义构造参数
viewmodel 类定义
必须是继承 AndroidViewModel 才可以
class CustomAndroidViewModel(private val app: Application, private val data: String) :
AndroidViewModel(app) {
fun print() {
logEE(data)
}
}
我们的 CustomAndroidViewModel 类中也有一个 data:String 参数,这个参数就是对应上一步中自定义工厂中的 data 参数
- 实例化 viewmodel
val model = ViewModelProvider(
viewModelStore,
CustomAndroidViewModelFactory(application, "自定义安卓工厂创建viewmodel")
).get(CustomAndroidViewModel::class.java)
model.print()
1.4、自定义简单工厂初始化
多次获取可以复用 model,不会重新创建
自定义简单工厂也是为了实现 viewmodel 构造传参的目的,废话不多说,直接上代码吧
- 定义安卓工厂
class CustomSimpleViewModelFactory(app:Application,private val data:String) : ViewModelProvider.AndroidViewModelFactory(app) {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return try {
modelClass.getConstructor(String::class.java).newInstance(data)
} ......
}
}
- viewmodel 类定义
class CustomSimpleViewModel(private val data: String) : ViewModel() {
fun print() {
logEE(data)
}
}
- 实例化 viewmodel
val model = ViewModelProvider(
viewModelStore,
CustomSimpleViewModelFactory(application, "自定义简单工厂创建viewmodel")
).get(CustomSimpleViewModel::class.java)
model.print()
1.5 使用委托机制创建 viewmodel(推荐)
多次创建可以复用 model,不会重新创建
google 官方给我们提供了 activity-ktx 库,通过它我们可以使用委托机制创建 viewmodel
实现方式如下:
- 加入依赖
implementation ''androidx.activity:activity-ktx:1.2.2''
- 创建 viewmodel 类
class EnTrustModel : ViewModel() {
fun print(){
logEE("关注公众号 \"安安安安卓\" 免费学知识")
}
}
- 实例化 viewmodel
private val wtModel: EnTrustModel by viewModels<EnTrustModel> {
ViewModelProvider.NewInstanceFactory()
}
- 调用 viewmodel 的方法
wtModel.print()
2、 viewmodel 概览
ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存。
我们知道 android 中的 Activity 和 Fragment 都是可以对生命周期进行管理的,如果 Activity/Fragment 被系统销毁,例如屏幕旋转就必须重新创建 Activity/Fragment,这个时候就涉及到一个数据恢复的问题。通常我们会使用 onSaveInstanceState 和 onRestoreSaveInstance 两个方法对数据进行保存和恢复,但是这两个方法只能处理数据量较小的情况。
如果是大数据量的情况那么用 viewmodel 就是不错的选择了
同时 viewmodel 还可以帮助我们将业务逻辑从 Activity/Fragment 中分离出来
3、 viewmodel 实现 Fragment 之间数据共享
同一个 activity 中出现多个 Fragment,并且这些 Fragment 之间需要进行通信,这样的需求是很常见的
以往我们可能会采用下列方式实现数据共享:
- EventBus
- 接口回调
- 全局共享数据池
但是当我们学了 viewmodel 后就会发现,viewmodel 可以轻松实现 Fragment 之间的数据共享
我们可以让多个 Fragment 共用同一个 ViewModel 实现数据的共享。
本例中我们要实现下面的功能:
activity 中有两个 fragment,FragmentA 和 FragmentB,我们在 ViewModel 的构造方法中开始倒计时 2000s,在 FragmentA 中观察倒计时的数据并展示在页面上。然后再切换到 FragmentB,如果 FragmentB 中的倒计时的秒数没有重置为 2000,说明我们的数据共享成功了。
上代码:
- 创建 ViewModel
class ShareDataModel: ViewModel() {
val liveData = MutableLiveData<String>()
var total = 2000L
init {
/**
* 实现倒计时,一秒钟倒计时一次
*/
val countDownTimer = object :CountDownTimer(1000 * total, 1000) {
override fun onTick(millisUntilFinished: Long) {
liveData.postValue("剩余倒计时时间 ${--total}")
}
override fun onFinish() {
logEE("倒计时完成")
}
}
countDownTimer.start()
}
}
- 创建 FragmentA
class FragmentA : Fragment() {
private val model: ShareDataModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_a, container, false).apply {
model.liveData.observe(viewLifecycleOwner,
{ value -> findViewById<TextView>(R.id.tv_aresult).text = value })
}
}
}
- 创建 FragmentB
class FragmentB : Fragment() {
private val model: ShareDataModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_b, container, false).apply {
model.liveData.observe(viewLifecycleOwner,
{ value -> findViewById<TextView>(R.id.tv_bresult).text = value })
}
}
}
- 展示结果
通过 gif 我们发现,即使 replace 后倒计时的数据仍然没有改变,说明我们成功实现了数据共享
4、 关于 viewmodel 的几个知识点
- viewmodel 绝对不可以引用视图
- 在 activity 的存在期间可能会多次走 onCreate 方法,但是我们的 viewmodel 只有 actvity 结束并销毁的情况下才会被回收
- viewmodel 通过 lifecycle 来观察 activity/fragment 的生命周期
5、源码解析
5.1、创建 ViewModel 源码解析
先看一下创建 ViewModel 的代码:
val model = ViewModelProvider(
viewModelStore,
CustomSimpleViewModelFactory(application, "自定义简单工厂创建viewmodel")
).get(CustomSimpleViewModel::class.java)
那么就从 ViewModelProvider 构造方法和 get 方法说起吧,
- ViewModelProvider 构造方法
ViewModelProvider 构造方法传入两个参数,ViewModelStore(用来存储管理 ViewModel)和 Factory(创建 ViewModel 的工厂)
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
- 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);
}
getCanonicalName 方法可以获取一个能唯一标识 Class<T>的字符串,最终会用作生成我们 ViewModelStore 中存储 ViewModel 的 key。
获取 key 后我们将 key 和 modelClass 做为参数调用重载 get 方法
- 重载 get 方法
方法的解读写在代码中了
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
ViewModel viewModel = mViewModelStore.get(key);//从ViewModelStore中根据key获取ViewModel,如果有就会返回
if (modelClass.isInstance(viewModel)) {//判断获取的ViewModel是否是传入的modelClass类型
if (mFactory instanceof OnRequeryFactory) {//不看这里
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;//如果是就返回viewmodel
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) mFactory).create(key, modelClass);//一般不会用这个工厂
} else {
viewModel = mFactory.create(modelClass);//创建ViewModel
}
mViewModelStore.put(key, viewModel);//将ViewModel添加到mViewModelStore中
return (T) viewModel;
}
5.2、ViewModelStore 类解析
ViewModelStore 中有三个重要方法 get、put、clear,并且维护了一个 HashMap<String,ViewModel>
- get
final ViewModel get(String key) {
return mMap.get(key);
}
很简单就是根据key获取ViewModel
- put
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);//oldViewModel指的是被覆盖的ViewModel
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
将一个 ViewModel 添加到 map 中,并且将被覆盖的 ViewModel 数据清理(因为同一个 activity 中只能存在一个同类型的 ViewModel)
- clear
下一节会讲
clear 方法的调用时机(重点)
先看一下 clear 方法
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
clear 方法的调用位置在 ComponentActivity 的构造方法中,代码如下:
getLifecycle().addObserver(new LifecycleEventObserver() {
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
if (event == Lifecycle.Event.ON_DESTROY) {
// Clear out the available context
mContextAwareHelper.clearAvailableContext();
// And clear the ViewModelStore
if (!isChangingConfigurations()) {//当有配置信息存在的时候返回:true(屏幕旋转,后台被杀死),当没有配置信息的时候返回:false(应用被主动杀死)。
getViewModelStore().clear();
}
}
}
});
恍然大悟,原来 ViewModel 通过 LifeCycle 观察 Activity 生命周期的方式来处理数据的清理操作
关注我的公众号 “安安安安卓” 免费学知识粗体
我们今天的关于Android架构组件JetPack之ViewModel(二)和android jetpack mvvm框架搭建的分享已经告一段落,感谢您的关注,如果您想了解更多关于Android Jetpack -- ViewModel篇(一)、Android Jetpack 架构组件 (一) 与 AndroidX、Android Jetpack-ViewModel、android Jetpack—ViewModel使用方法和详细原理解析的相关信息,请在本站查询。
本文标签: