浅析系列(1)——ViewModel和LiveData

原文链接:https://juejin.im/post/5a4f5ebf6fb9a01cad7bf979

一、介绍

安卓开发的痛点:

我们都知道安卓为了解决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…


转载于:https://juejin.im/post/5a4f5ebf6fb9a01cad7bf979

展开阅读全文
博主设置当前文章不允许评论。

win2k源码浅析1

03-01

可以看出来,为了和win 9x保持兼容,win2000的很多函数可以说什么也没做,只是调用了rn另一个API,如:rnrnHWNDGetLastActivePopup(rn HWNDhwnd)rnrnPWNDpwnd=ValidateHwnd(hwnd);rnrnif(pwnd==NULL)rnreturnNULL;rnrnpwnd=_GetLastActivePopup(pwnd);rnrnreturnHW(pwnd);rnrnrnrnBOOLWINAPIAdjustWindowRectEx(rnLPRECTlpRect,rnDWORDdwStyle,rnBOOLbMenu,rnDWORDdwExStyle)rnrnConnectIfNecessary();rnrnreturn_AdjustWindowRectEx(lpRect,dwStyle,bMenu,wExStyle);rnrnrnWORDGetWindowWord(rnHWNDhwnd,rnintindex)rnrnPWNDpwnd;rnrnpwnd=ValidateHwnd(hwnd);rnrnif(pwnd==NULL)rnreturn0;rnrn/*rn*Ifit’sadialogwindowthewindowdataisontheserversidern*Wejustcallthe"long"routineinsteadofhavetwothunks.rn*WeknowthereisenoughdataifitsDWLP_USERsowewon’tfault.rn*/rnif(TestWF(pwnd,WFDIALOGWINDOW)&&(index==DWLP_USER))rnreturn(WORD)_GetWindowLong(pwnd,index,FALSE);rnrnrnreturn_GetWindowWord(pwnd,index);rnrnrnrn等等rn 论坛

没有更多推荐了,返回首页