由于MVP固有的一些因素,比如随着项目越来越大P层越来越膨胀和内存泄露问题,MVVM就面世了。MVVM组成:Model,ViewModel,View,其实和MVP差不多,就是把Presenter换成了VIewModel。简单的概括关系就是:
M --> VM --> V,
M被VM持有,由VM来处理业务逻辑,而VM又被V持有,实际上是形成一条单向线的。不过VM和V会通过binder来实现双向绑定,这样也就进一步达到了解耦的目的。配上一张看到的很形象的图:
由于谷歌爸爸提供的JetPack库,我们可以很容易的拿到相应的组件来实现。简单的提一下相关的组件:
ViewModel、LiveData/DataBinding、
ViewModel
作为核心成员,实际上是一个抽象类:
/**
* 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实现双向绑定的方法。
附一张作用域图:
简单使用:
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
这里要注意的是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的继承关系:
这样就很清晰了,也就是说我们的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 super T> 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需要重写的方法。
后记
不知不觉,这篇文章居然在草稿箱里躺了至少快两年,当初是想说感觉写的还不够成熟想在完善一下再发,没想到一搁置就这么久...懒真的是要命啊啊啊ε=ε=ε=(#>д<)ノ