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 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

更多相关文章

  1. mybatisplus的坑 insert标签insert into select无参数问题的解决
  2. python起点网月票榜字体反爬案例
  3. 类和 Json对象
  4. 《Android开发从零开始》——25.数据存储(4)
  5. Android系统配置数据库注释(settings.db)
  6. Android中不同应用间实现SharedPreferences数据共享
  7. Andorid Dialog 示例【慢慢更新】
  8. android图表ichartjs
  9. Android内容提供者源码

随机推荐

  1. [置顶] 使用BleLib的轻松搞定Android低功
  2. 情况控件Android layout_weight用法图解
  3. android:launchMode — “standard” “s
  4. Android自动化测试初探(四): 模拟键盘鼠标
  5. 2013.03.19(8)———android 打开url 浏览
  6. Android培训班(13)
  7. 几个标签属性的意义
  8. Android手势检测简介
  9. android下拉刷新android-Ultra-Pull-To-R
  10. android 连接服务器的方法及安全性问题