Android MVVM框架之Livedata以及ViewModel

背景

场景1:

假设现在我们在Activity或者fragment当中会监听数据Model的回调,在回调里面会更新一些UI的状态,那么就会存在以下问题:

1、当页面不可见的时候也是能收到回调的,相当于在后台进行UI刷新,这个其实是不对的。一般情况下如果想解决这个问题的话就需要在onStop的时候反注册Model监听,然后等页面回来onStart的时候重新注册Model监听且需要主动读取一次Model数据进行ui刷新,听起来是不是很麻烦?

2、在onDestory里面会需要主动反注册Model的回调,如果忘记反注册就会导致内存泄漏问题

针对这个问题,google在jetpack组件的Android架构组件当中提供了LiveData类来解决这个问题

场景2

Activity或者Fragment在后台的时候可能由于资源不足导致销毁重新创建,就会导致界面数据丢失问题,对于简单的数据,Activity 可以使用 onSaveInstanceState() 方法从 onCreate() 中的Bundle恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图

针对这个问题google在jetpack组件的Android架构组件当中提供了ViewModel类来解决这个问题

MVVM框架

大家可以看下google推荐的Android架构图,如下所示。ViewModel和LiveData是一起配套使用的,组成了MVVM架构:
架构图

使用方式

ViewModel及LiveData的接入方式参考:
https://developer.android.com/jetpack/androidx/releases/lifecycle

下面看下使用方式:
1、首先自定义一个ViewModel类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class NameViewModel extends ViewModel {

// Create a LiveData with a String
private MutableLiveData<String> currentName;

public MutableLiveData<String> getCurrentName() {
if (currentName == null) {
currentName = new MutableLiveData<String>();
}
return currentName;
}

// Rest of the ViewModel...
}

自定义一个NamaViewModel继承自ViewModel,稍后会对ViewModel源码进行解析。里面有一个MutableLiveData对象,它是LiveData子类,稍后会对LiveData源码进行解析。

2、接下来看下这个NameViewModel的使用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class NameActivity extends AppCompatActivity {

private NameViewModel model;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Other code to setup the activity...

// Get the ViewModel.
model = new ViewModelProvider(this,new NewInstanceFactory()).get(NameViewModel.class);

// Create the observer which updates the UI.
final Observer<String> nameObserver = new Observer<String>() {
@Override
public void onChanged(@Nullable final String newName) {
// Update the UI, in this case, a TextView.
nameTextView.setText(newName);
}
};

// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.getCurrentName().observe(this, nameObserver);
}
}

可以看出从ViewModelProvider里面根据class类型到了一个NameViewModel,然后给里面的LiveData注册了一个观察者Observer,在这个Observer会更新TextView的显示文本

3、更新数据:

1
2
3
4
5
6
7
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String anotherName = "John Doe";
model.getCurrentName().setValue(anotherName);
}
});

更新数据的方式也很简单,调用LiveData里面的setValue方法即可,如果是在子线程,就调用postValue方法

以上就是ViewModel+LiveData的使用方式,接下来分别对LiveData及ViewModel进行源码剖析

LiveData

LiveData为什么可以解决开头提到的第一个问题场景1

来看下官网关于LiveData的描述:

LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
如果观察者(由 Observer 类表示)的生命周期处于 STARTED 或 RESUMED 状态,则 LiveData 会认为该观察者处于活跃状态。LiveData 只会将更新通知给活跃的观察者。为观察 LiveData 对象而注册的非活跃观察者不会收到更改通知

看起来很厉害的样子,接下来从源码的角度来进行分析:

LiveData源码解析

首先是observer方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
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);
}

可以看到observer方法传入了2个参数: LifecycleOwner(如果对LifecycleOwner不太了解,参考之前的博文:Android生命周期感应组件lifecycle )和Observer接口,Oberver就是一个简单的观察接口,通过泛型代表具体的数据类型:

1
2
3
4
5
6
7
public interface Observer<T> {
/**
* Called when the data is changed.
* @param t The new data
*/
void onChanged(@Nullable T t);
}

方法主要分为几部分:

1、如果当前LifecycleOwner是destoryed的状态就直接返回
2、生成一个LifecycleBoundObserver对象wrapper,放入mObservers这个Map当中
3、最后把wrapper加入到Lifecycle里面,从这里可以看出LifecycleBoundObserver实现了Lifecycle接口

解析来看下这个LifecycleBoundObserver对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
@NonNull final LifecycleOwner mOwner;

LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) {
super(observer);
mOwner = owner;
}

@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}

@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
//1、在LifecycleOwner销毁了之后会自动移除obverver,解决内存泄漏问题
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}

@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}

@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}

private abstract class ObserverWrapper {
final Observer<T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION;

ObserverWrapper(Observer<T> observer) {
mObserver = observer;
}

abstract boolean shouldBeActive();

boolean isAttachedTo(LifecycleOwner owner) {
return false;
}

void detachObserver() {
}

void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += mActive ? 1 : -1;
//变成active状态
if (wasInactive && mActive) {
onActive();
}
//变成InActive状态
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
//2、从InActive变成Active,会自动dispatchVaule,也就是页面可见的时候就会自动收到最新的数据回调
if (mActive) {
dispatchingValue(this);
}
}
}

从以上代码可以看出,LifecycleBoundObserver实现了Lifecycle生命周期接口,能够自动感应生命周期的变化,并且解决了最开始场景1提出的2个问题:

1、在LifecycleOwner销毁了之后会自动移除obverver,解决内存泄漏问题
2、从InActive变成Active,会自动dispatchVaule,也就是页面可见的时候就会自动收到最新的数据回调

我们还注意到LiveData有两个方法:onActive和onInactive,这两个方法在LiveData是空方法,子类可以覆写这俩个方法,在里面做一些注册和反注册的操作,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class StockLiveData extends LiveData<BigDecimal> {
private StockManager stockManager;

private SimplePriceListener listener = new SimplePriceListener() {
@Override
public void onPriceChanged(BigDecimal price) {
setValue(price);
}
};

public StockLiveData(String symbol) {
stockManager = new StockManager(symbol);
}

@Override
protected void onActive() {
stockManager.requestPriceUpdates(listener);
}

@Override
protected void onInactive() {
stockManager.removeUpdates(listener);
}
}

接下来是setValue以及postValue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
//noinspection unchecked
setValue((T) newValue);
}
};

protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

/**
* Sets the value. If there are active observers, the value will be dispatched to them.
* <p>
* 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);
}

可以看出postValue用于在子线程调用的场景,post到主线程执行setValue。这里大家可能有个疑问就是这个postValue以及setValue都是protected方法,外部怎么调用呢?一般情况下使用MutableLiveData:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* {@link LiveData} which publicly exposes {@link #setValue(T)} and {@link #postValue(T)} method.
*
* @param <T> The type of data hold by this instance
*/
@SuppressWarnings("WeakerAccess")
public class MutableLiveData<T> extends LiveData<T> {
@Override
public void postValue(T value) {
super.postValue(value);
}

@Override
public void setValue(T value) {
super.setValue(value);
}
}

以上就是LiveData源码分析结果,可以看出主要还是借助了Lifecycle生命周期自动感应的特性来解决问题。LiveData还有一些高级的玩法,具体参考:
https://developer.android.com/topic/libraries/architecture/livedata

ViewModel

ViewModel为什么可以解决开头提到的第二个问题场景2

来看下官网关于ViewModel的描述:

ViewModel 类旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转等配置更改后继续留存。

先来回顾一下ViewModel的使用方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class NameActivity extends AppCompatActivity {

private NameViewModel model;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Other code to setup the activity...

// Get the ViewModel.
// 只要是同一个Activity,数据就是同一份
model = new ViewModelProvider(this,new NewInstanceFactory()).get(NameViewModel.class);

// Create the observer which updates the UI.
final Observer<String> nameObserver = new Observer<String>() {
@Override
public void onChanged(@Nullable final String newName) {
// Update the UI, in this case, a TextView.
nameTextView.setText(newName);
}
};

// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.getCurrentName().observe(this, nameObserver);
}
}

可以看出如果重新创建了NameActivity,它接收的NameViewModel实例与第一个NameActivity创建的实例相同,这样就数据就不会存在丢失的问题。当所有者NameActivity走了finish销毁之后,框架会调用ViewModel对象的onCleared()方法,以便它可以清理资源。

来看下ViewModel的生命周期

ViewModel生命周期

ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle。ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时。如下图所示:
ViewModel生命周期

在系统首次调用 Activity 对象的 onCreate() 方法时请求 ViewModel。系统可能会在 Activity 的整个生命周期内多次调用 onCreate(),如在旋转设备屏幕时。ViewModel 存在的时间范围是从首次请求 ViewModel 直到 Activity 完成并销毁。

接下来看下对ViewModel源码进行解析:

ViewModel源码解析

ViewModel:

1
2
3
4
5
6
7
8
9
10
11
public abstract class ViewModel {
/**
* This method will be called when this ViewModel is no longer used and will be destroyed.
* <p>
* It is useful when ViewModel observes some data and you need to clear this subscription to
* prevent a leak of this ViewModel.
*/
@SuppressWarnings("WeakerAccess")
protected void onCleared() {
}
}

可以看出ViewModel本身的话比较简单,就是一个抽象类,里面有一个onCleared()方法,子类在这个方法里面可以做一些资源清理的操作

ViewModel有一个子类AndroidViewModel:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class AndroidViewModel extends ViewModel {
@SuppressLint("StaticFieldLeak")
private Application mApplication;

public AndroidViewModel(@NonNull Application application) {
mApplication = application;
}

/**
* Return the application.
*/
@SuppressWarnings("TypeParameterUnusedInFormals")
@NonNull
public <T extends Application> T getApplication() {
//noinspection unchecked
return (T) mApplication;
}
}

一般情况下我们使用的时候是直接继承自AndroidViewModel,因为里面持有Application,可以获取到Android系统的一些系统资源

那么到这里大家就会有个疑问,ViewModel就这么简单?ViewModel的onCleared()方法是谁负责调用的呢?

首先ViewModel的获取方式为:

1
model = new ViewModelProvider(this,new NewInstanceFactory()).get(NameViewModel.class);

ViewModelProvider

我们来看下ViewModelProvider构造函数:

1
2
3
4
5
6
7
8
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}

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

可以看出传入的第一个参数为一个接口ViewModelStoreOwner,说明Activity实现了这个接口,返回一个ViewModelStore类型,第二个参数是一个工厂类,代表model创建工厂,系统提供了两个默认的工厂,分别是NewInstanceFactory用于创建普通的ViewModel,AndroidViewModelFactory用于创建AndroidViewModel

接下来看下get方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull Class<T> 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 extends ViewModel> T get(@NonNull String key, @NonNull Class<T> 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.
}
}

viewModel = mFactory.create(modelClass);
mViewModelStore.put(key, viewModel);
//noinspection unchecked
return (T) viewModel;
}

可以看出get方法就是从ViewModleStore里面根据key获得一个ViewModel返回,如果获取不到就通过factory创建一个ViewModel放入ViewModelStore当中,然后返回ViewModel。

ViewModelStore

那么这里我们来看下ViewModelStore是个什么东东:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class ViewModelStore {

private final HashMap<String, ViewModel> 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);
}

/**
* Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.onCleared();
}
mMap.clear();
}
}

可以看到ViewModelStore里面其实就是一个HashMap,存有这个ViewModelProvider里面所有的ViewModel,一个ViewModelProvider跟一个页面进行对应。可以看到ViewModelStore里面还有一个clear方法,里面就调用了每个ViewModel的onCleared()方法,根据前面对ViewModel生命周期的说明,这个clear方法应该是在lifecycle的onDestroyed方法调用的。

通过Android Studio的find usage找到这个clear()方法的调用链,发现在lifecycle的extension包下面找到了一个叫做HolderFragment的类:

HolderFragment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class HolderFragment extends Fragment implements ViewModelStoreOwner {
..............

private ViewModelStore mViewModelStore = new ViewModelStore();

................
@Override
public void onDestroy() {
super.onDestroy();
mViewModelStore.clear();
}

/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
return sHolderFragmentManager.holderFragmentFor(activity);
}

/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(Fragment fragment) {
return sHolderFragmentManager.holderFragmentFor(fragment);
}

在这个fragment的onDestroy()方法里面调用了ViewModelStore的clear方法。熟悉lifecycle的朋友看到这里是不是有一种豁然开朗的感觉,这个HolderFragment肯定是跟ViewModelProvider对应的页面进行了绑定!那么HolderFragment是啥时候跟页面进行绑定的呢?

来看下holderFragmentFor(FragmentActivity activity)调用的地方:

ViewModelStores

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@SuppressWarnings("WeakerAccess")
public class ViewModelStores {

@NonNull
@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
if (activity instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) activity).getViewModelStore();
}
return holderFragmentFor(activity).getViewModelStore();
}

@NonNull
@MainThread
public static ViewModelStore of(@NonNull Fragment fragment) {
if (fragment instanceof ViewModelStoreOwner) {
return ((ViewModelStoreOwner) fragment).getViewModelStore();
}
return holderFragmentFor(fragment).getViewModelStore();
}
}

可以看出这个ViewModelStores是一个工具类,用于生成ViewModelStore,而这个ViewModelStore就是从HolderFragment获取的,这个HolderFragment就挂载在这个fragment或者activity上面,接下来看下ViewModelStores的of方法调用的地方:

ViewModelProviders

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class ViewModelProviders {

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
return of(fragment, null);
}

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
return of(activity, null);
}

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
Application application = checkApplication(checkActivity(fragment));
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
@Nullable Factory factory) {
Application application = checkApplication(activity);
if (factory == null) {
factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}
return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
}

可以看出这个ViewModelProviders是一个工具类,用于生成ViewModelProvider,这里面就会调用ViewModelStores的of方法获取ViewModelStore,而我们刚刚也说了这个of方法里面就会挂载一个HolderFragment用于感应对应页面的生命周期。因此,要想自定义的ViewModel在页面销毁的时候能够自动调用onCleared()方法,得通过ViewModelProviders来获取:

1
viewModelProvider= ViewModelProviders.of(this);

至此ViewModel源码分析完成,更多高级玩法参考:
https://developer.android.com/topic/libraries/architecture/viewmodel

总结

至此LiveData+ViewModel源码已经分析完成,结合之前的Android生命周期感应组件lifecycle,整个就组成了MVVM架构。

这个架构里面很多思想值得我们细细品味~~

基于这个LiveData这些优良的特性,美团技术团队开发了一个LiveDataEventBus来代替传统的EventBus、RxJava等消息框架,感兴趣的可以看看:
https://tech.meituan.com/2018/07/26/android-livedatabus.html

https://github.com/JeremyLiao/LiveEventBus