Android(安卓)使用Google官方组件搭建 MVVM架构
Android 使用Google官方组件搭建 MVVM架构
概述
MVVM架构在MVP结构上,将Presenter改为ViewModel,使用ViewModel与View进行(DataBinding—>双向?)绑定,当ViewModel的数据发生更改,自动反映到View中显示;
反之,当View对数据更改时,ViewModel的数据也会随之变化。
View: 对应于Activity和XML,负责View的绘制以及与用户交互。 Model: 实体模型。 ViewModel: 负责完成 View与Model间的交互,负责业务逻辑。
初识Android MVVM 时,百度搜到一大堆DataBinding数据绑定的文章,很容易陷入泥潭。
事实上,DataBinding 只是实现数据和UI双向绑定的框架,是构建MVVM模式的一个工具,可以单独使用在任何架构中,也可以在MVVM中使用,增加代码简洁性。
ViewModel
ViewModel类旨在存储和管理用户界面相关的数据生命周期中的方式,在ViewModel类中当屏幕旋转配置更改时允许数据能够被保存。
比如:在一个Activity中包含一个用户的表单数据。当Activity为改变配置重新创建时(屏幕旋转为例),新建的Activity会重新获取用户的数据。对于简单的数据可以保存在onSaveInstanceState()方法并在onCreate()中通过bundle获取数据,但是这种方式只适合少量数据的序列化和反序列化,对于大量的表单数据或者图片不适用。
-
SavedState 会经由System Server(一个独立的进程)保存内容(序列化的数据),也就是说,他会无视应用进程的存在。
UI的状态记录,比如选择区域和滚动距离。
导航状态键值记录。
异常终止时保存数据
-
而ViewModel则一直运行在应用进程中,即便应用的配置发生变化,只要进程还在,ViewModel保存的内容就不会消失。但只要进程消失。ViewModel的内容也会消失。
保留应用对于网络、数据库的请求。
当作大型数据的缓存。
-
ViewModel层只做和业务逻辑和业务数据相关的事请,不做任何UI操作。ViewModel层不会持有任何控件的引用,更不会在ViewModel中通过UI控件的引用去更新UI
-
ViewModel 的生命周期:
注意:
-
任何时候都不要将Context传入ViewModel;
-
如果要在ViewModel中使用Application实例,请使用 AndroidViewModel 子类;
-
当进程关闭时,ViewModel会被销毁,而onSaveInstanceState不会受影响,所以用SavedState
存储少量关键数据,并在场景恢复后用来加载页面数据。
View
View层不做任何逻辑业务、不涉及操作数据、不处理数据、将UI和数据严格地分开。
- View层主要负责和UI相关的工作,我们只在XML、Activity和Fragment中写View层的代码,View层不做任何业务相关的事情;
- 更新UI通过数据绑定实现,尽量在ViewModel里面做,即更新绑定的数据源即可;
- Activity中做初始化一些控件的工作;
Model
Model层最大的特点是被赋予了数据获取的职责,与平常Model层只是dinginess实体对象的行为截然不同。
实际中,数据的获取、存储、数据状态变化都是Model层 的工作。
- Model层包括实体模型(Bean)、Retrofit的Service,获取网络数据的接口,本地存储(增删改查)接口,数据变化监听等。
- Model层提供数据接口供ViewModel调用,经过数据转换并最终映射绑定到View层某个UI元素的属性。
协同合作
LiveData
LiveData是一个可观测数据的容器类。LiveData能够感知Activtiy、Fragment或者Service的生命周期,因此,LiveData确保了只有在这些组件处于活动状态的时候才会更新他们。
-
确保UI和数据的状态保持一致
LiveData符合观测者的行为规则。当生命周期的状态产生变化时,他就会通知Observer对象。
LiveData的观测者会在每次数据产生变化时更新UI,从而我们无需再数据变化的每个地方添加更新UI的代码。
-
不会内存泄漏
观测者受到LifeCycle的作用域限制,并且会在与其关联的生命周期被销毁后自动清理自己。
-
资源共享
-
无需管理生命周期
创建 LiveData 对象
public class NameViewModel extends ViewModel { // 创建一个存储 String 的 LiveData 对象 private MutableLiveData<String> mCurrentName; public MutableLiveData<String> getCurrentName() { if (mCurrentName == null) { mCurrentName = new MutableLiveData<String>(); } return mCurrentName; }// ViewModel 的其余部分……}
在初始状态,LiveData
对象中的数据没有被赋值。
注意:请务必将更新UI的LiveData对象存储在ViewModel对象中,而并非Activtiy或者Fragment。
原因如下:
- 避免过于臃肿的 Activity 和 Fragment 。这些UI控制器仅仅负责展示数据,而非存储数据。
- 将 LiveData 实例从特定的Activity或Fragment中解耦出来,并允许LiveData存活周期更长(例:activity配置更改时)。
观察 LiveData 对象
通常,我们应当在某个应用组件的 **onCreate() **方法中对设置对 LiveData 对象的观察。
- 确保系统不会因为执行 activity 或 fragment 的 onResume() 方法而导致重复调用观察。
- 确保 activity 或 fragment 能在进入活动状态时尽快获得用于展示 UI 的数据。
一般而言,LiveData 只有在数据变化的情况下才会将更新通知给活动状态的观察者。如果观察者又一次从非活动状态进入活动状态,那么只有当数据在这期间又发生变化,观察者才会收到更新。
LiveData只能在生命周期active下发送数据给观察者,Activity处于后台(inactive)时,即使LiveData收到了新的数据,也不会通知Activity;但是当Activity重新返回到前台(active)会受到新的数据。如我们所说,LiveData是粘性的。
public class NameActivity extends AppCompatActivity { private NameViewModel mModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 其他设置 activity 的代码…… // 获取 ViewModel mModel = ViewModelProviders.of(this).get(NameViewModel.class); // 创建用于更新 UI 的观测者 final Observer<String> nameObserver = new Observer<String>() { @Override public void onChanged(@Nullable final String newName) { // 更新UI mNameTextView.setText(newName); } }; // 将这个 activity 作为 LifecycleOwner 参数,和观测者一并传入,开始观测该 LiveData mModel.getCurrentName().observe(this, nameObserver); }}
更新LiveData对象
LiveData没有提供公开可用的更新其数据的方法;但是LiveData的子类 MutableLiveData 类 则把 setValue(T) 和 postValue(T) 公开暴露出来;
mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String anotherName = "Naco Siren"; //通知观察者自动更新UI mModel.getCurrentName().setValue(anotherName); }});
注意:在主线程中,必须调用 setValue(T) 来更新LiveData对象。 工作线程中,可以使用 postValue(T) 来更新LiveData对象。
扩展LiveData
有时候我们需要在 observer 的 LifeCycle 处于 active 状态时做一些操作,那么需要继承LiveData或者MutableLiveData,重写父类的 onActive() 和 onInactive() 方法。这两个方法默认实现为空。
public class StockLiveData extends LiveData<BigDecimal> { private StockManager mStockManager; private SimplePriceListener mListener = new SimplePriceListener() { @Override public void onPriceChanged(BigDecimal price) { setValue(price); } }; public StockLiveData(String symbol) { mStockManager = new StockManager(symbol); } @Override protected void onActive() { mStockManager.requestPriceUpdates(mListener); } @Override protected void onInactive() { mStockManager.removeUpdates(mListener); }}
转换 LiveData
某些情况下,需要将 LiveData 中存储的数据转换处理后再分发给 Observer。可以通过Transformartions 类的
map
操作符来实现。
原始处理:
1 mNumberLiveData = new MutableLiveData<>();23 mNumberLiveData.observe(this, new Observer<Integer>() {4 @Override5 public void onChanged(@Nullable Integer integer) {6 mTvNumber.setText("" + integer);7 Log.d(TAG, "onChanged: " + integer);8 }9 });
Transformartions map 处理:
1 mNumberLiveData = new MutableLiveData<Integer>(); 2 3 Transformations.map(mNumberLiveData, new Function<Integer, String>(){ 4 @Override 5 public String apply(Integer integer) { 6 return "" + integer; 7 } 8 }).observe(this, new Observer<String>() { 9 @Override10 public void onChanged(@Nullable String s) {11 mTvNumber.setText(s);12 Log.d(TAG, "onChanged: " + s);13 }14 });
-
Transformartions.map()
把一个函数变换应用到
LiveData
对象所存储的值,并将结果向下传递。 -
Transformartions.switchMap()
与
map()
相似地,把一个函数变换应用到 LiveData 对象所存储的值,并将结果拆包后向下传递。该函数必须返回一个 LiveData 对象
MediatorLiveData
LiveData包中的 Transformations 只提供了
map()
和switchMap()
两个方法。如果要有更复杂的需求,必须通过 MediatorLiveData 类来创建自己的transformations
。
Transformations 源码:
1 public class Transformations { 2 3 private Transformations() { 4 } 5 6 7 @MainThread 8 public static <X, Y> LiveData<Y> map(@NonNull LiveData<X> source, 9 @NonNull final Function<X, Y> func) {10 final MediatorLiveData<Y> result = new MediatorLiveData<>();11 result.addSource(source, new Observer<X>() {12 @Override13 public void onChanged(@Nullable X x) {14 result.setValue(func.apply(x));15 }16 });17 return result;18 }192021 @MainThread22 public static <X, Y> LiveData<Y> switchMap(@NonNull LiveData<X> trigger,23 @NonNull final Function<X, LiveData<Y>> func) {24 final MediatorLiveData<Y> result = new MediatorLiveData<>();25 result.addSource(trigger, new Observer<X>() {26 LiveData<Y> mSource;2728 @Override29 public void onChanged(@Nullable X x) {30 LiveData<Y> newLiveData = func.apply(x);31 if (mSource == newLiveData) {32 return;33 }34 if (mSource != null) {35 result.removeSource(mSource);36 }37 mSource = newLiveData;38 if (mSource != null) {39 result.addSource(mSource, new Observer<Y>() {40 @Override41 public void onChanged(@Nullable Y y) {42 result.setValue(y);43 }44 });45 }46 }47 });48 return result;49 }50}
合并多个 LiveData 数据源
MediatorLiveData 是 LiveData 的一个子类,允许合并多个 LiveData 数据源。当任何LiveData 数据源对象发生改变时,MediatorLiveData 对象的观察者都会被通知。
- 一个与数据库中的数据关联的
LiveData
对象 - 一个与从网络获取的数据关联的
LiveData
对象
MediatorLiveData<User> data = new MediatorLiveData(); data.addSource(LocalModel.getUserInfo(), new Observer<User>() { @Override public void onChanged(@Nullable User user) { data.setValue(user); } }); data.addSource(NetModel.getUserInfo(), new Observer<User>() { @Override public void onChanged(@Nullable User user) { data.setValue(user); } }); data.observe(this,new Observer<User>() { @Override public void onChanged(@Nullable User user) { // 更新UI } });
如果想在数据更新的时候让 Observer 立即得到通知,也就是说忽略生命周期状态,我们可以使用LiveData 的 observerForver(Observer
方法。
参考文档
- Google JetPack 指南 https://developer.android.google.cn/jetpack
- LiveData && ViewModel 使用详解 https://zhuanlan.zhihu.com/p/61893868
- Android 之 MVVM 架构指南LiveData https://juejin.im/post/5d833f446fb9a06b1829f372
更多相关文章
- mybatisplus的坑 insert标签insert into select无参数问题的解决
- python起点网月票榜字体反爬案例
- 类和 Json对象
- 《Android开发从零开始》——25.数据存储(4)
- Android系统配置数据库注释(settings.db)
- Android中不同应用间实现SharedPreferences数据共享
- Andorid Dialog 示例【慢慢更新】
- android图表ichartjs
- Android内容提供者源码