Android :安卓第一行代码学习笔记之 ViewModel组件的简单理解和使用
ViewModel组件ViewModel组件参考ViewModel组件参考1、Android架构之ViewModel组件2、Android之ViewModel的使用3、Android ViewModel,再学不会你砍我4、Android mvvm框架之ViewModel篇5、Android JetPack组件之ViewModel的使用详解...
ViewModel组件
ViewModel组件
1、什么是 ViewModel
1.1、先考虑两个场景
- 场景一:我们开发的 APP 可以转屏,转屏后将触发
Controller(Activity or Fragment)
的重建,为了维护转屏前后数据的一致性,我们要么将需要维护的数据以Bundle
的形式在onSaveInstance
中保存,必要的时候需要对复合数据实现繁琐的Parceable
接口,如果数据量太大则我们必须将数据持久化,在转屏后重新拉取数据(from database or networks); - 场景二:我们的 Activity 中同时维护了多个 Fragment,每个 Fragment 需要共享一些数据,传统的做法是由宿主Activity 持有共享数据,并暴露数据获取接口给各个寄生 Fragment。
随着业务规模的扩大,以上的两种场景在传统实现方法中显得越来越繁琐且不易维护,且数据模块不易独立进行测试。
特别说明:
- 关于场景一,同样的场景还适用于各种
配置相关的信息发生变化
的情况,比如键盘、系统字体、语言区域
等,它们的共同作用是都会导致当前Controller 的重建。
1.2、视图与数据模型之间的桥梁ViewModel
在页面(Activity/Fragment)功能较为简单的情况下,我们通常会将UI交互
、与数据获取
等相关的业务逻辑全部写在页面中。但是在页面功能复杂的情况下,代码量会变的非常多,也违反了"单一功能原则"
。 页面只应该负责处理用户与UI控件的交互,并将数据展示到屏幕上,而数据获取相关的业务逻辑应该单独处理和存放。
ViewModel 解决的问题:
ViewModel 是 android 新的 mvvm 框架的一部分,它的出现就是为了解决以上两个场景中数据与 Controller 耦合过度的问题。
其 基本原理 是
:维护一个与配置无关的对象,该对象可存储 Controller 中需要的任何数据,其生命周期与宿主Controller 的生命周期保持一致,不因 Controller 的重建而失效
(注意:Controller 的重建仍然在 Controller 生命周期内,并不会产生一个新的生命周期,即 Controller 的 onDestroy 并不会调用)
这意味着无论是转屏还是系统字体变化等因配置变化产生的 Controller 重建都不会回收 ViewModel 中维护的数据,重建的 Controller 仍然可以从同一个 ViewModel 中通过获取数据恢复状态。
2、ViewModel的生命周期
ViewModel
的生命周期会比创建它的Activity、Fragment
的生命周期都要长。即ViewModel
中的数据会一直存活在Activity/Fragment
中。
3、ViewModel的使用
1、首先引入ViewModel的相关依赖
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
2、布局文件activity_view_model.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ViewModelActivity">
<TextView
android:id="@+id/tv_figure"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="0"
android:textSize="20sp" />
<Button
android:id="@+id/btn_add1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="40dp"
android:text="+1" />
<Button
android:id="@+id/btn_add2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="100dp"
android:text="+2" />
</RelativeLayout>
3、创建MyViewModel类继承于ViewModel
public class MyViewModel extends ViewModel {
public int number=0;
}
4、ViewModelActivity
public class ViewModelActivity extends AppCompatActivity {
private TextView tv_figure;
private Button btn_add1, btn_add2;
private MyViewModel myViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_model);
tv_figure = findViewById(R.id.tv_figure);
btn_add1 = findViewById(R.id.btn_add1);
btn_add2 = findViewById(R.id.btn_add2);
//这个获取MyViewModel对象的方法已经被废弃了
// myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
//创建MyViewModel对象
myViewModel = new ViewModelProvider(this).get(MyViewModel.class);
tv_figure.setText(String.valueOf(myViewModel.number));
btn_add1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
myViewModel.number++;
tv_figure.setText(String.valueOf(myViewModel.number));
}
});
btn_add2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
myViewModel.number += 2;
tv_figure.setText(String.valueOf(myViewModel.number));
}
});
}
}
实验结果:
4、ViewModel的原理
我们用一个结构图来剖析 ViewModel 的构造过程:
如图所示:
- 所有已经实例化的 ViewModel 都缓存在一个叫做 ViewModelStore 的封装对象中,其实质是一个 HashMap;
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);
}
Set<String> keys() {
return new HashSet<>(mMap.keySet());
}
/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
- ViewModelStore 与具体的 Controller 绑定,并与宿主 Controller 俱生俱灭,所以这就解释了为何ViewModel 与宿主 Controller 的生命周期是一样长了,因为缓存它的 ViewModelStore 与宿主 Controller 寿命相等;
- 获取 ViewModel 实例的过程委托给了一个叫做 ViewModelProvider 的工具类,它包含一个创建 ViewModel的工厂类 Factory 和一个对 ViewModelStore 的引用;
总的构造过程为:先从 ViewModelStore 中获取缓存的 ViewModel,若没有缓存过则用 Facotry 实例化一个新的 ViewModel 并缓存,具体的过程分为 4 步,具体可参考图示。
本小节剩下部分分析源码,对于只关心原理的同学此部分可以略过:
我们在获取 ViewModel 的时候,一般通过如下方式:
// 在 Controller(这里以 Fragment 为例)的 onCreate 方法中调用
final UserModel viewModel = ViewModelProviders.of(this).get(UserModel.class);
我们看一下 ViewModelProviders.of() 的实现:
public static ViewModelProvider of(@NonNull Fragment fragment) {
return of(fragment, null);
}
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
// 最终用宿主 Controller 的 ViewModelStore 和一个 Factory 实例化一个
// ViewModelProvider
return new ViewModelProvider(fragment.getViewModelStore(), factory);
}
我们再看一下 ViewModelProvider.get() 方法获取 ViewModel 实例的过程:
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");
}
// 我们看到了 ViewModel 在 ViewModelStore 中的 key 表示
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.
}
}
// 缓存中没有,通过 Factory 构造
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
// 新实例保存缓存
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}
4.1、问题
看到这里,大家是否都有个疑问,明明ViewModelProvider需要传入ViewModelStoreOwner对象,那为什么我们第三节的demo中传入this(当前Activity)
就可以实例化呢?
myViewModel = new ViewModelProvider(this).get(MyViewModel.class);
因为ViewModelActivity
继承自AppCompatActivity
, AppCompatActivity
继承自FragmentActivity
,FragmentActivity
继承自ComponentActivity
,而ComponentActivity
实现了ViewModelStoreOwner
接口,这个接口中有一个getViewModelStore
()方法返回的是ViewModelStore
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
LifecycleOwner,
ViewModelStoreOwner,
HasDefaultViewModelProviderFactory,
SavedStateRegistryOwner,
OnBackPressedDispatcherOwner
.....
@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;
}
参考
1、Android架构之ViewModel组件
2、Android之ViewModel的使用
3、Android ViewModel,再学不会你砍我
4、Android mvvm框架之ViewModel篇
5、Android JetPack组件之ViewModel的使用详解
更多推荐
所有评论(0)