一、介绍
安卓开发的痛点:
我们都知道安卓为了解决ANR的问题,通常的做法是:UI线程刷新View,工作者线程(io线程)进行读取数据库,获取网络数据等耗时工作。但这也伴随而来一个问题,由于数据获取和UI更新的任务在不同的线程执行,很有可能出现一种情况:数据获取完成,准备刷新Activity/Fragment的View时,它们已经销毁了,如果我们在更新view时没有判断当前Activity/fragment是否Active并作出相应的操作,或者没有对Activity/fragment进行弱引用处理,很有可能造成内存泄漏。
其次,还有一种很罕见的情况(个人认为无关痛痒),当安卓activity重建(比如旋转)时它自身会destroy掉,再次Create,在这个过程中android系统会在 onSaveInstanceState()方法中,利用Bundle为我们保存一些简单的数据,但对于大量的数据Bundle可能就无能无力了。遇到这种场景,一般是用Loader解决,或者干脆在Activity created时获取一次数据即可。就我个人而言,google的架构解决activity重建(不是杀死进程后的activity重建)可能是多此一举的。
讲到这里,其实可以知道,google官方架构组件最主要的目的就是解决这两大问题。
二、ViewModel
首先ViewModel的诞生是为了解决第二个问题,就是Activity销毁重建的问题,在这我先放出一张官方图,从这张官方提供的ViewModel生命周期图中,可以很清楚地看出ViewModel的作用:
不得不说,google为了实现ViewModel生命周期的管理,利用了Fragment的一个小特性,如果我们在Fragment实例化的时候调用setRetainInstance(true),该Fragment在activity重建的时候根本不会被销毁,就是说该Fragment的onDestroy和onCreate在activity重建时不会被执行。所以呢,google “撮合” ViewModel和Fragment为has a的关系(整体和部分的关系)。Fragment的生命周期就是ViewModel的生命周期。
此时ViewModel就拥有了如上图所示的生命周期了。其他的都是一些细节上的处理,比如ViewModel在Framgent的onDestroy中执行clear操作,再比如其实Fragment是间接持有ViewModel对象(Fragment持有的是ViewModelStore的实例,而ViewModelStore持有ViewModel的对象)。
至此,我们简单的分析下,当应用crash或内存不足进程被杀死的情况,Fragment实例肯定不存在了,ViewModel也就不可能存在。所以ViewModel可能只适用于Activity重建(但UI线程还存在)的情况。
接下来,我们感性的分析下性能,使用ViewModel必须创建Fragment(Fragment使用还是个大坑),但Fragment不需要渲染UI,对性能的影响不算太大,最多是内存多了几个对象。所以ViewModel虽然是google出品,但并没有像想象当中的那么优秀(估计这就是一直没开源的原因)。
三、LiveData
LiveData如果配合上ArchTaskExecutor(google 开发的任务执行类,可以切换io线程和ui线程),基本上可以算的是精简版的Rxjava,如果熟悉Rxjava的童鞋可以很轻松地理LiveData,首先我们从字面上理解LiveData,Live是实时的意思,顾名思义LiveData就是说数据是实时更新给观察者的(android中一般就是view了),我先贴出一段简单的代码,简单的感受下LiveData:
// Observe product data
model.getObservableProduct().observe(this, new Observer<ProductEntity>() {
@Override
public void onChanged(@Nullable ProductEntity productEntity) {
model.setProduct(productEntity);
}
});复制代码
在这里我不去的分析LiveData的源码了,估计网上类似文章有不少,只挑几个LiveData实现比较精巧的点分享一下:
1)LiveData是怎么解决数据和UI生命周期不一致的问题的(第一节介绍中的第一个问题),其实关键就在LifecycleOwner这个interface上,我们看一段observe方法(绑定观察者与被观察者之间的关系):
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
......
owner.getLifecycle().addObserver(wrapper);
}
复制代码
observe方法中的参数owner,可以认为它是activity/fragment,因为在v4包(我用的版本是26.1.0)中Fragment和FragmentActivity默认实现了LifecycleOwner interface。所以我们可以通过owner.getLifecycle().getCurrentState()的方式获取Activity当前的状态,同时做出相应的防内存泄漏操作就可以了。
至于LifeCycles这个组件,我们平常写Activity的时候(不用v4包)也可以继承它,把各个生命周期的状态赋值给CurrentState,确实对在多个对象间传递activity生命周期状态有所帮助。
2)其次再说一个有意思的点,LiveData有两个主要方法,setValue和postValue,其中setValue是UI主线程执行,postValue可以在io线程执行(其就是通过Handler将值传递主线程)。在google的代码注释有有这么一段:
/**
* Posts a task to a main thread to set the given value. So if you have a following code
* executed in the main thread:
* <pre class="prettyprint">
* liveData.postValue("a");
* liveData.setValue("b");
* </pre>
* The value "b" would be set at first and later the main thread would override it with
* the value "a".
* <p>
* If you called this method multiple times before a main thread executed a posted task, only
* the last value would be dispatched.
*
* @param value The new value
*/
复制代码
按字面意思理解,就是说如果我先调用liveData.postValue("a"),之后调用liveData.setValue("b"),在主线程可能发现他的值为“a”,这就是多线程更新同一个data的可能产生的情况。
比较有趣的是第二点,如果我们多次postValue,在main thread未执行posted task之前,它收到的是最近一次postValue传递的值(最后一次调用)。
google为了实现这个功能,在这个过程中其实做了加锁操作:
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
复制代码
private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
//noinspection unchecked
setValue((T) newValue);
}
};
复制代码
看着这两段代码,其实是通过postTask的状态判断当前的mPostValueRunnable有没有被主线程的looper处理,换句话说就是判断setValue((T) newValue)是否执行。
并且通过两个加锁操作保证postTask的更新对其他线程是可见的,也就是保证mPendingData赋值操作的原子性。并且加锁的粒度细化到赋值操作,写的很巧妙,值得学习。
四、ViewModel和LiveData的关系
ViewModel在我看来像是一个容器,它存储着LiveData的对象,保证LiveData的生命周期跟它一样长,所以在我看来,ViewModel我们可以不用,LiveData对于轻量级的app,
可以选择替换掉Rxjava,至于lifecycles,如果我们使用v4包的activity和Fragment,其实对我们使用LiveData没有影响,近似于透明。
五、参考
如有纰漏,欢迎斧正。
(1) developer.android.com/topic/libra…
(2)stackoverflow.com/questions/1…