Android实现MVVM

MVVM简介:

MVVM(Model-View-ViewModel) 是由Mvp演变而来。

  • View包含Ui布局,以及布局生命周期控制器(Activity,Fragment)

  • DataBinding实现view层与viewModel数据的双向绑定(但实际上在Android Jetpack中DataBinding只存在于布局和布局生命周期控制器之间,当数据变化绑定到布局生命周期控制器时再转发给ViewModel,布局控制器可以持有DataBinding但ViewModel不应该持有DataBinding)

  • ViewModel负责处理数据和实现业务逻辑,但是ViewModel层不应该直接或者间接地持有View层的任何引用,因为一个ViewModel不应该直达自己具体是和哪一个View进行交互的.ViewModel主要的工作就是将Model提供来的数据直接翻译成View层能够直接使用的数据,并将这些数据暴露出去,同时ViewModel也可以发布事件,供View层订阅.

ViewModel:

ViewModel是一个可以用来存储UI相关的数据的类。ViewModel的生命周期会比创建它的Activity、Fragment的生命周期长,禁止在ViewModel的持有Activity、Fragment、View等对象,这样会引起内存泄漏。如果需要在ViewModel中引用Context的话,google为我们提供了AndroidViewModel 类来供我们使用。
在这里插入图片描述

ViewModel优点:

  1. 不会因为屏幕旋转或者其他配置改变(比如切换到多窗口模式)而销毁,避免数据重新创建,比如网络数据重新加载的情况。
  2. 当Activity或者Fragment的正常销毁的时候,自动清除数据,也就是绑定了Activity或者Fragment的生命周期,避免了内存泄漏。
  3. 多个Fragment之间或者Activity和Fragment之间更好地传递数据,进行交互。

官方案例:在 Fragment 之间共享数据
Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的情况。想象一下主从 Fragment 的常见情况,假设您有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。这种情况不太容易处理,因为这两个 Fragment 都需要定义某种接口描述,并且所有者 Activity 必须将两者绑定在一起。此外,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。

可以使用 ViewModel 对象解决这一常见的难点。这两个 Fragment 可以使用其 Activity 范围共享 ViewModel 来处理此类通信,如以下示例代码所示:

    public class SharedViewModel extends ViewModel {        private final MutableLiveData selected = new MutableLiveData();        public void select(Item item) {            selected.setValue(item);        }        public LiveData getSelected() {            return selected;        }    }    public class MasterFragment extends Fragment {        private SharedViewModel model;        public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {            super.onViewCreated(view, savedInstanceState);            model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);            itemSelector.setOnClickListener(item -> {                model.select(item);            });        }    }    public class DetailFragment extends Fragment {        public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {            super.onViewCreated(view, savedInstanceState);            SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);            model.getSelected().observe(getViewLifecycleOwner(), { item ->               // Update the UI.            });        }    }    

**请注意:**这两个 Fragment 都会检索包含它们的 Activity。这样,当这两个 Fragment 各自获取 ViewModelProvider 时,它们会收到相同的 SharedViewModel 实例(其范围限定为该 Activity)。

此方法具有以下优势:

  • Activity 不需要执行任何操作,也不需要对此通信有任何了解。
  • 除了 SharedViewModel 约定之外,Fragment不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。
  • 每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个Fragment,界面将继续工作而没有任何问题。

ViewModel 和 onSaveInstanceState 区别

ViewModel

  1. 缓存在内存中的,相当于本地缓存和网络缓存读写比较快
  2. 可以存较大的值,比如bitmap、大对象等等
  3. 数据绑定了Activity或者Fragment的生命周期,当Activity正常关闭的时候,都会清除ViewModel中的数据.比如
    | 调用finish()
    || 按返回键退出,
    || 用户直接杀进程或者是放后台内存不足,被回收了
  4. 在Activity旋转或者其他配置改变的时候,ViewModel里面是还有值得,不会销毁,
  5. ViewModel也可以使用本地存储,通过SavedStateHandle实现的,使用SavedStateViewModelFactory这个工厂,这里就不多介绍了,已经是正式版本了,引用是implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0

onSaveInstanceState

  1. 仅适合可以序列化再反序列化的少量数据
  2. 不适合数量可能较大的数据,如用数组或Bitmap。可以存一些简单的基本类型和简单的小对象、例如字符串,和一些id
  3. 会把数据存储到磁盘上

注意: 对于复杂的大型数据,请使用 数据库 或者 SharedPreferences

LiveData

LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
特点:

  • 数据可以被观察者订阅;
  • 能够感知组件(Fragment、Activity、Service)的生命周期;
  • 只有在组件出于激活状态(STARTED、RESUMED)才会通知观察者有数据更新;

使用LiveData的理由:

  • 能够保证数据和UI统一,

    这个和LiveData采用了观察者模式有关,LiveData是被观察者,当数据有变化时会通知观察者(UI)。

  • 减少内存泄漏

这是因为LiveData能够感知到组件的生命周期,当组件处于DESTROYED状态时,观察者对象会被清除掉。

  • 当Activity停止时不会引起崩溃

这是因为组件处于非激活状态时,不会收到LiveData中数据变化的通知。

  • 不需要额外的手动处理来响应生命周期的变化

这一点同样是因为LiveData能够感知组件的生命周期,所以就完全不需要在代码中告诉LiveData组件的生命周期状态。

  • 组件和数据相关的内容能实时更新

组件在前台的时候能够实时收到数据改变的通知,这是可以理解的。当组件从后台到前台来时,LiveData能够将最新的数据通知组件,这两点就保证了组件中和数据相关的内容能够实时更新。

  • 针对configuration change时,不需要额外的处理来保存数据

我们知道,当你把数据存储在组件中时,当configuration change(比如语言、屏幕方向变化)时,组件会被recreate,然而系统并不能保证你的数据能够被恢复的。当我们采用LiveData保存数据时,因为数据和组件分离了。当组件被recreate,数据还是存在LiveData中,并不会被销毁。

  • 资源共享

通过继承LiveData类,然后将该类定义成单例模式,在该类封装监听一些系统属性变化,然后通知LiveData的观察者,这个在继承LiveData中会看到具体的例子。
源码分析:

添加观察者

LiveData提供了两种添加观察者的方法:observeForever()、observe()。

  • observeForever()
@MainThreadpublic void observeForever(@NonNull Observer observer) {    observe(ALWAYS_ON, observer);}

从方法的命名,我们也能对它的功能略知一二,通过observeForever()添加观察者,观察者会一直受到数据的变化回到,而不是在组件处于STARTED和RESUMED状态下才会收到,因为这是LifecycleOwner对象就不再是组件了,而是ALWAYS_ON;另外通过该方法添加观察者后,要手动调用removeObserver()方法来停止观察者接收回调通知。observeForever()方法体很简单,调用了observe()方法,传入的一个参数是ALWAYS_ON常量,重点看下ALWAYS_ON常量是个啥东东。

private static final LifecycleOwner ALWAYS_ON = new LifecycleOwner() {    private LifecycleRegistry mRegistry = init();    private LifecycleRegistry init() {        LifecycleRegistry registry = new LifecycleRegistry(this);        registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);        registry.handleLifecycleEvent(Lifecycle.Event.ON_START);        registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);        return registry;    }    @Override    public Lifecycle getLifecycle() {        return mRegistry;    }};

ALWAYS_ON是LifecycleOwner常量,在init方法中会初始化Lifecycle的生命周期状态,完了之后,就没有改变过Lifecycle的生命周期状态了,这也就是为什么通过observeForever()添加观察者是,当数据改变时不管组件处于什么状态都会收到回调的原因,除非手动将观察者移除。

  • observe()
@MainThreadpublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {    if (owner.getLifecycle().getCurrentState() == DESTROYED) {        // ignore        return;    }    //将LifecycleOwner对象和Observer对象封装成LifecycleBoundObserver对象。    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);    // mObservers可以理解成一个类似Map的容器,putIfAbsent()方法是判断容器中的observer(key)    // 是否有已经和wrapper(value)关联,如果已经关联则返回关联值,否则关联并返回wrapper。    LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);    if (existing != null && existing.owner != wrapper.owner) {        throw new IllegalArgumentException("Cannot add the same observer"                + " with different lifecycles");    }    if (existing != null) {        return;    }    owner.getLifecycle().addObserver(wrapper); //条件LifecycleOwner的生命周期观察者}

setValue

 /**     * Sets the value. If there are active observers, the value will be dispatched to them.     * 

* This method must be called from the main thread. If you need set a value from a background * thread, you can use {@link #postValue(Object)} * * @param value The new value */ @MainThread protected void setValue(T value) { assertMainThread("setValue"); mVersion++; mData = value; dispatchingValue(null); }

**dispatchingValue(null)**方法用于通知观察者

组件(Fragment/Activity)生命周期发生变化

在LiveData.observe()方法中添加了组件(实现了LifecycleOwner接口的Fragment和Activity)生命周期观察者。而这个观察者就是LifecycleBoundObserver对象,下面我们看下LifecycleBoundObserver具体实现。

class LifecycleBoundObserver implements GenericLifecycleObserver {    public final LifecycleOwner owner;    public final Observer observer;    public boolean active;    public int lastVersion = START_VERSION;    LifecycleBoundObserver(LifecycleOwner owner, Observer observer) {        this.owner = owner;        this.observer = observer;    }    @Override    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {        // LifecycleOwner对象生命周期发生变化时,会通过该回调方法通知过来。        // 如果当前处于Lifecycle.State.DESTROYED时,会自动将观察者移除。        if (owner.getLifecycle().getCurrentState() == DESTROYED) {            removeObserver(observer);            return;        }        // 判断是否处于actived状态,并将结果作为参数传递给activeStateChanged()        activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));    }    void activeStateChanged(boolean newActive) {        if (newActive == active) { //新状态和之前状态相同,不处理            return;        }        active = newActive;        boolean wasInactive = LiveData.this.mActiveCount == 0;        LiveData.this.mActiveCount += active ? 1 : -1;        if (wasInactive && active) { //处于激活状态的observer个数从0到1            onActive();        }        if (LiveData.this.mActiveCount == 0 && !active) { //处于激活状态的observer个数从1变为0            onInactive();        }        if (active) {            dispatchingValue(this);         }    }}

通过observe()方法添加观察者时,当组件(Fragment/Activity)生命周期发生变化时,onStateChanged()方法会被调用。如果通过observeForever()方法添加观察者时,只有在常量ALWAYS_ON初始化的时候,onStateChanged()方法会被调用三次(CREATED、STARTED、RESUMED),后面就不会收到DESTROYED的状态,这也就是为什么要通过removeObserver()方法手动移除观察者的原因。
onActive()和onInactive()都是空实现的方法,继承类可以选择去实现。我们看下dispatchingValue()方法的具体实现。

private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) {    if (mDispatchingValue) {        mDispatchInvalidated = true;        return;    }    mDispatchingValue = true;    do {        mDispatchInvalidated = false;        if (initiator != null) {  // initiator不为空,考虑通知回调            considerNotify(initiator);            initiator = null;        } else { // initiator为空,考虑通知mObservers容器中对象回调            for (Iterator, LifecycleBoundObserver>> iterator =                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {                considerNotify(iterator.next().getValue());                if (mDispatchInvalidated) {                    break;                }            }        }    } while (mDispatchInvalidated);    mDispatchingValue = false;}private void considerNotify(LifecycleBoundObserver observer) {    if (!observer.active) {        return;    }    if (!isActiveState(observer.owner.getLifecycle().getCurrentState())) {        observer.activeStateChanged(false);        return;    }    if (observer.lastVersion >= mVersion) {        return;    }    observer.lastVersion = mVersion;    // 最终回调的地方,也就是调用observe()或者observeForever()是传入的Observer对象。    observer.observer.onChanged((T) mData);}

改变LiveData数据

LiveData提供了两种改变数据的方法:setValue()和postValue()。区别是setValue()要在主线程中调用,而postValue()既可在主线程也可在子线程中调用。我们先看setValue()方法的具体实现:

@MainThreadprotected void setValue(T value) {    assertMainThread("setValue"); //判断当前线程是否是主线程,不是主线程就抛出异常    mVersion++;    mData = value;    dispatchingValue(null);}

再看下postValue()方法的具体实现:

protected void postValue(T value) {    boolean postTask;    synchronized (mDataLock) {        postTask = mPendingData == NOT_SET;        mPendingData = value;    }    if (!postTask) {        return;    }    // 会在主线程中执行  mPostValueRunnable中的内容。    ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable); }private final Runnable mPostValueRunnable = new Runnable() {    @Override    public void run() {        Object newValue;        synchronized (mDataLock) {            newValue = mPendingData;            mPendingData = NOT_SET;        }        // 在主线程中调用setValue()方法        setValue((T) newValue);     }};

postValue()方法通过ArchTaskExecutor实现在主线程中执行mPostValueRunnable对象中的内容,而在mPostValueRunnable中最终会调用setValue()方法来实现改变LiveData存储的数据。

转换 LiveData

您可能希望在将 LiveData 对象分派给观察者之前对存储在其中的值进行更改,或者您可能需要根据另一个实例的值返回不同的 LiveData 实例。Lifecycle 软件包会提供 Transformations 类,该类包括可应对这些情况的辅助程序方法。

Transformations.map()
对存储在 LiveData 对象中的值应用函数,并将结果传播到下游。

    LiveData userLiveData = ...;    LiveData userName = Transformations.map(userLiveData, user -> {        user.name + " " + user.lastName    });    

Transformations.switchMap()
与 map() 类似,对存储在 LiveData 对象中的值应用函数,并将结果解封和分派到下游。传递给 switchMap() 的函数必须返回 LiveData 对象,如以下示例中所示:

    private LiveData getUser(String id) {      ...;    }    LiveData userId = ...;    LiveData user = Transformations.switchMap(userId, id -> getUser(id) );    

更多相关文章

  1. Android里监视数据库的变化 registerContentObserver 接口
  2. Android下的数据储存方式(三)
  3. android中将数据写入手机内存和sdcard中的文件
  4. Android settings.db数据库中添加一条新的默认配置项
  5. Android之解析Json数据
  6. 《Android》Lesson08-Activity的生命周期
  7. Notification中Intent携带数据重复问题
  8. ThreadLocal原理解析(1):数据存取

随机推荐

  1. Android(安卓)studio 解决各种错误
  2. Android Listener侦听的N种写法
  3. android Shader类简介_渲染图像
  4. 深入ListView
  5. Android(安卓)Eclipse 闪退问题
  6. 异常 报错 Android library projects can
  7. Android AsyncTask问题
  8. android MotionEvent详解
  9. Android布局居中的几种做法
  10. Kotlin编程之AndroidStudio(包括3.0与2.x