MVVM浅析

由于MVP固有的一些因素,比如随着项目越来越大P层越来越膨胀和内存泄露问题,MVVM就面世了。MVVM组成:Model,ViewModel,View,其实和MVP差不多,就是把Presenter换成了VIewModel。简单的概括关系就是:
M --> VM --> V,
M被VM持有,由VM来处理业务逻辑,而VM又被V持有,实际上是形成一条单向线的。不过VM和V会通过binder来实现双向绑定,这样也就进一步达到了解耦的目的。配上一张看到的很形象的图:


mvvm-arch.jpg

由于谷歌爸爸提供的JetPack库,我们可以很容易的拿到相应的组件来实现。简单的提一下相关的组件:
ViewModel、LiveData/DataBinding、

ViewModel

作为核心成员,实际上是一个抽象类:


ViewModel.jpg
/**
 * ViewModel is a class that is responsible for preparing and managing the data for
 * an {@link android.app.Activity Activity} or a {@link androidx.fragment.app.Fragment Fragment}.
 * It also handles the communication of the Activity / Fragment with the rest of the application
 * (e.g. calling the business logic classes).
 * 

* A ViewModel is always created in association with a scope (an fragment or an activity) and will * be retained as long as the scope is alive. E.g. if it is an Activity, until it is * finished. *

* In other words, this means that a ViewModel will not be destroyed if its owner is destroyed for a * configuration change (e.g. rotation). The new instance of the owner will just re-connected to the * existing ViewModel. *

* The purpose of the ViewModel is to acquire and keep the information that is necessary for an * Activity or a Fragment. The Activity or the Fragment should be able to observe changes in the * ViewModel. ViewModels usually expose this information via {@link LiveData} or Android Data * Binding. You can also use any observability construct from you favorite framework. *

* ViewModel's only responsibility is to manage the data for the UI. It should never access * your view hierarchy or hold a reference back to the Activity or the Fragment. *

* ViewModels can also be used as a communication layer between different Fragments of an Activity. * Each Fragment can acquire the ViewModel using the same key via their Activity. This allows * communication between Fragments in a de-coupled fashion such that they never need to talk to * the other Fragment directly. *

*/ public abstract class ViewModel { ... }

经典的注解更重要系列,甚至连用法都告诉我们了但太长被我截了(不自觉得跪在了地上)。
注解:

1.ViewModel是一个类,负责为Activity或Fragment准备和管理数据。它还处理Activity/Fragment与应用程序其余部分的通信(例如,调用业务逻辑类)。

2.ViewModel始终与范围(片段或活动)相关联地创建,并且只要范围是活动的,就将被保留。例如。如果是活动,则直到完成。换句话说,这意味着如果ViewModel的所有者因配置更改(例如旋转)而被销毁,则不会销毁它。所有者的新实例将重新连接到现有的ViewModel。

3.ViewModel的目的是获取并保留Activity或Fragment所需的信息。Activity或Fragment应该能够观察 ViewModel中的更改。 ViewModel通常通过{@link LiveData}或Android Data Binding公开此信息。您也可以使用自己喜欢的框架中的任何可观察性构造。

4.ViewModel的唯一责任是管理UI的数据。它绝不能访问您的视图层次结构或保留对活动或片段的引用。

5.ViewModels也可以用作Activity的不同Fragment之间的通信层。每个Fragment都可以通过其Activity使用相同的键获取ViewModel。这允许Fragment之间以解耦的方式进行通信,从而使它们无需直接与另一个片段交谈。

这样看来,ViewModel的职责就很清晰了,就是替Activity或Fragment管理数据的,也就是前面说的相当于Presenter,用来处理业务逻辑,所以本身并没有很神秘。注意一下第三点,View层是应该能观察到ViewModel中的数据更改的,可以通过LiveData、DataBinding或者其他的观察者模式框架来实现,这也就是View和ViewModel实现双向绑定的方法。

附一张作用域图:


ViewModelScope.png

简单使用:

public class UserModel extends ViewModel {

    @Override
    protected void onCleared() {
        super.onCleared();
        //do cleared work
    }

    void doAction(){
        // depending on the action, do necessary business logic calls 
        // and update the userLiveData.
    }
}

还是借鉴一下官网的例子:

public class MyViewModel extends ViewModel {
    private MutableLiveData> users;
    public LiveData> getUsers() {
        if (users == null) {
            users = new MutableLiveData>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // Do an asynchronous operation to fetch users.
        //这里就可以做一些异步操作来获取数据
    }
}
public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.

        MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

就是这么简单,就把界面和业务逻辑分离了,这里用到了LiveData,后面再说。重写onCleared()做一些资源的释放,以及我们自己提供一些业务逻辑的操作。onCleared()会在Activity/Fragment被回收时调用,这是怎么做到的呢?

先看下ViewModel是怎么获取的:

UserModel userModel = new ViewModelProvider(this, new UserModelFactory()).get(UserModel.class);

引入implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'库则可以使用单参数构造器:

UserModel userModel = new ViewModelProvider(this).get(UserModel.class);

通过ViewModelProvider的get()方法来获取,先看下这个ViewModelProvider


ViewModelProvider.png

这里要注意的是androidx.lifecycle:lifecycle-viewmodel:2.2.0包才提供了单参数的构造方法。

get()方法:

    @NonNull
    @MainThread
    public  T get(@NonNull Class 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);
    }

    @NonNull
    @MainThread
    public  T get(@NonNull String key, @NonNull Class 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.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }

可以看到,先根据类名和DEFAULT_KEY进行拼装得到key,然后在去mViewModelStore里取。如果取出来的viewModel是我们要的类,那就直接返回(此时viewModel可能为null),否则就“log a warning”。

接着判断持有的mFactory是否是KeyedFactory的类型,如果是就通过这个Factory来构造出viewModel,如果是别的类型,就直接create。然后把viewModel再放进mViewModelStore里,其实这里也就表明了,如果不同的Activity/Fragment去获取同类型的ViewModel的话,那么取到的实例(引用)就是一样的,那当然持有的数据就是一致的,也就能达到通信的效果。

而ViewModelProvider之所以为我们提供了Factory,就是因为在获取ViewModel的时候我们是不能直接通过new来用构造器创建的,这样的话获取的都是全新的ViewModel对象,那就没有意义了。所以由Factory来提供,并可以让我们创建有参的ViewModel。(这里保留观点,待核实)

看下ViewModelStore:

public class ViewModelStore {

    private final HashMap 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 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();
    }
}

很简单,通过HashMap来存ViewModel,注意一下clear()方法,会将持有的VM都进行清理

    @MainThread
    final void clear() {
        mCleared = true;
        // Since clear() is final, this method is still called on mock objects
        // and in those cases, mBagOfTags is null. It'll always be empty though
        // because setTagIfAbsent and getTag are not final so we can skip
        // clearing it
        if (mBagOfTags != null) {
            synchronized (mBagOfTags) {
                for (Object value : mBagOfTags.values()) {
                    // see comment for the similar call in setTagIfAbsent
                    closeWithRuntimeException(value);
                }
            }
        }
        onCleared();
    }

可以看到clear()内部就调用了给我们自己实现的onCleared()方法,到这里我们只知道逻辑,但还是不知道调用时机。回过头来看下ViewModelProvider的构造函数:

public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }

提供了两种构造函数,但最终都是通过ViewModelStore和Factory来构造的。那这个ViewModelStoreOwner又是什么呢,其实看名字就应该知道,就是用来提供ViewModelStore的。

public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}

实际上,就是一个接口。看下它的实现类,就是ComponentActivity:

    @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;
    }

重写了该方法,目的也就是为了提供mViewModelStore。再看下ComponentActivity的继承关系:


ComponentActivity.png

这样就很清晰了,也就是说我们的Activity也实现了ViewModelStoreOwner接口,再看下ComponentActivity的构造函数:

public ComponentActivity() {
        Lifecycle lifecycle = getLifecycle();
        //noinspection ConstantConditions
        if (lifecycle == null) {
            throw new IllegalStateException("getLifecycle() returned null in ComponentActivity's "
                    + "constructor. Please make sure you are lazily constructing your Lifecycle "
                    + "in the first call to getLifecycle() rather than relying on field "
                    + "initialization.");
        }
      
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });
    }

这里删减了其他不相关的代码,可以看到,持有了Lifecycle实例,通过对生命周期的监听(这里是ON_DESTROY),来实现clear逻辑。这里还要注意的一点是,当配置改变的时候(翻转),是不会去清理的。这也很好理解,因为只是界面重绘,数据如果重新获取(ViewModel)那还是一样的数据。

到这里,为什么ViewModel能在Activity被回收时去清理资源就很清晰了。

LiveData

上面也有提到,其实真正存储数据的是LiveData。先从用法入手:

      userModel.liveData.observe(this, new Observer() {
            @Override
            public void onChanged(Integer integer) {
                textView.setText(String.valueOf(integer));
            }
        });

LiveData由ViewModel来持有,为其添加观察者并指定动作则可以监听数据,当数据变化时我们就可以更新UI或者做其他的事情,经典的观察者模式。

public abstract class LiveData {
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    final Object mDataLock = new Object();
    static final int START_VERSION = -1;
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    static final Object NOT_SET = new Object();

    private SafeIterableMap, ObserverWrapper> mObservers =
            new SafeIterableMap<>();

    // how many observers are in active state
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    int mActiveCount = 0;
    private volatile Object mData;
    // when setData is called, we set the pending data and actual data swap happens on the main
    // thread
    @SuppressWarnings("WeakerAccess") /* synthetic access */
    volatile Object mPendingData = NOT_SET;
    private int mVersion;

    private boolean mDispatchingValue;
    @SuppressWarnings("FieldCanBeLocal")
    private boolean mDispatchInvalidated;

    private final Runnable mPostValueRunnable = new Runnable() {
        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            setValue((T) newValue);
        }
    };

    /**
     * Creates a LiveData initialized with the given {@code value}.
     *
     * @param value initial value
     */
    public LiveData(T value) {
        mData = value;
        mVersion = START_VERSION + 1;
    }

    /**
     * Creates a LiveData with no value assigned to it.
     */
    public LiveData() {
        mData = NOT_SET;
        mVersion = START_VERSION;
    }
}

注释比较长就没贴了,大致如下:

1.LiveData是可以在给定生命周期内观察到的数据持有者类。这意味着可以将{@link Observer}与{@link LifecycleOwner}成对添加,并且只有配对的LifecycleOwner处于活动状态时,才会向该观察者通知有关包装数据的修改。如果LifecycleOwner的状态为{@link Lifecycle.StateSTARTED}或{@link Lifecycle.StateRESUMED},则将其视为活动状态。通过{@link watchForever(Observer)}添加的观察者被视为始终处于活动状态,因此将始终收到有关修改的通知。对于这些观察者,您应该手动调用{@link removeObserver(Observer)}。

2.如果相应的生命周期变为{@link Lifecycle.StateDESTROYED}状态,则添加了生命周期的观察者将被自动删除。这对于活动和片段可以安全地观察LiveData而不用担心泄漏的活动特别有用:销毁它们时,它们将立即被取消订阅。

3.此外,LiveData具有{@link LiveData#onActive()}和{@link LiveData#onInactive()}方法,可在活动{@link Observer}的数量在0到1之间变化时得到通知。这就允许LiveData可以释放大量的资源,当没有任何正在观察的观察者时。

4.此类旨在容纳{@link ViewModel}的各个数据字段,但也可以用于以解耦的方式在应用程序中的不同模块之间共享数据。

几个重要的字段:
mDataLock:设值时的锁
START_VERSION:数据(消息)起始版本
mObservers:观察者集合SafeIterableMap,其实是由双向链表来伪装成的Map
mData:持有的数据
mVersion:当前数据版本
mPostValueRunnable:提供设值逻辑

有两个构造函数,带值的就直接将值赋给mData,并将起始版本号加1(因为已经有数据了嘛);无参构造函数就正常初始化。

我们就先从observe()方法看起,这也是第一条注释提到的,可以将Observer(观察者)和LifecycleOwner(生命周期持有者)成对添加。

   /**
    * @param owner    The LifecycleOwner which controls the observer
     * @param observer The observer that will receive the events
     */
    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {
        assertMainThread("observe");
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // ignore
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }

从名字也可以看出,这个方法就是用来添加(注册)观察者的,而LifecycleOwner则是对应的观察者的生命周期持有者,是一个有关生命周期的接口,由ComponentActivity实现。目的很简单,匿名内部类持有外部类引发的内存泄漏,以及当我们页面都已经关闭了,那么再来接收事件(更新界面)那就没有意义了。

首先assertMainThread()方法就是来判断当前是否为主线程,如果不是就抛出异常(这里因该是持有了LifecycleOwner的原因)。接着判断当前LifecycleOwner的状态,如果是DESTROYED状态就直接return掉。

紧接着封装了一个LifecycleBoundObserver对象,主要提供了解绑Observer方法,然后添加到mObservers中。如果观察者已经存在并且绑定到了owner,那就抛出异常。

最后拿到Lifecycle(抽象类,实现类为LifecycleRegistry)对象,调用addObserver()添加wrapper,这里主要就是监听生命周期状态。

还提供了observeForever(),也就是无论生命周期状态如何都一直监听,那就需要我们自己去移除监听者,这里就不看了。

接着看下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); }

设置值,如果有存活的observer就去分发。一样先判断是否是主线程,将mVersion加1(相当于这是新版本的数据),将新值赋给mData,进行分发:

void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
                for (Iterator, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

在setValue()方法中调用dispatchingValue()时传入了null,所以先看initiator为null的情况。很简单,就是拿到mObservers的迭代器,然后调用considerNotify()方法判断这个Observer是否要通知,看下considerNotify()方法:

private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
        //
        // we still first check observer.active to keep it as the entrance for events. So even if
        // the observer moved to an active state, if we've not received that event, we better not
        // notify for a more predictable notification order.
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        observer.mObserver.onChanged((T) mData);
    }

首先还是先判断了observer是否是Active状态,如果不是就调用observer.activeStateChanged(false)去更新状态;
接着判断observer的mLastVersion是否大于或等于LiveData的mVersion,如果是就直接return。也就是说,数据(消息)的版本(mVersion)要大于监听者的版本(mLastVersion )时,才会去做数据的分发通知,我们只对新版本的数据感兴趣(也可以把mVersion当做最新的消息版本,把mLastVersion当做上一次的消息版本,只有新消息的版本大于上一次消息的版本,我们才去通知)。其实这里也是引发粘性事件的地方,EventBus也是。往回看就可以知道,不管是observer.mLastVersion还是mVersion,它们的起始值都是START_VERSION(-1),而在setValue()的时候,mVersion进行了自加,也就是第一次的时候就变为了0,而mLastVersion并没有改动。那么这就导致了,即使是后于设值注册的observer,走到这里的时候,mLastVersion(-1)是小于mVersion(0)的,这也就让数据正常的往下走通知了出去。这也是为什么我们在使用EventBus去发消息的时候,明明消息已经发出去了,而新开启的Activity注册之后仍然能接收到的原因。当然这点也可以利用来作为Activity间开启的数据传递。

接着就是将mVersion赋给mLastVersion,告诉observer:这个数据已经旧了,下次要获取就要获取新的。然后回调observer的onChanged()方法把数据更新出去,这也就是一开始调用observe()来添加observer需要重写的方法。

后记

不知不觉,这篇文章居然在草稿箱里躺了至少快两年,当初是想说感觉写的还不够成熟想在完善一下再发,没想到一搁置就这么久...懒真的是要命啊啊啊ε=ε=ε=(#>д<)ノ

你可能感兴趣的:(MVVM浅析)