上一篇文章讲到了Android架构组件之一Lifecycle(Android 官方架构组件(一)——Lifecycle),现在我们再来看看另一个成员LiveData。
系列文章
Android 官方架构组件(三)——ViewModel


LiveData是google发布的lifecycle-aware components中的一个组件,除了能实现数据和View的绑定响应之外,它最大的特点就是具备生命周期感知功能

LiveData的优点

  • 能够确保数据和UI统一
    LiveData采用了观察者模式,当数据发生变化时,主动通知被观察者。
  • 解决内存泄露问题
    由于LiveData会在Activity/Fragment等具有生命周期的lifecycleOwner组件调用onDestory的时候自动解绑,所以解决了可能存在的内存泄漏问题。之前我们为了避免这个问题,一般有注册绑定的地方都要解绑(即注册跟解绑要成对出现),而LiveData利用生命周期感知功能解决了这一问题,可以实现只需关心注册,而解绑会根据生命周期自动进行的功能。
  • 当Activity停止时不会引起崩溃
    当Activity组件处于inactive非活动状态时,它不会收到LiveData数据变化的通知。
  • 不需要手动处理生命周期的变化
    观察者并不需要手动处理生命周期变化对自身的逻辑的影响,只需要关心如何处理获取到的数据。LiveData能够感知Activity/Fragment等组件的生命周期变化,所以就完全不需要在代码中告诉LiveData组件的生命周期状态,当数据发生变化时,只在生命周期处于active下通知观察者,而在inactive下,不会通知观察者。
  • 确保总能获取到最新的数据
    什么意思呢?第一种情况,当观察者处于active活动状态。LiveData基于观察者模式,所以当数据发生变化,观察者能够马上获取到最新变化;第二种情况,当观察者处于inactive非活动状态。LiveData只能生命周期active下发送数据给观察者。举个例子,当Activity处于后台(inactive)时,LiveData接收到了新的数据,但这时候LiveData并不会通知该Activity,但是当该Activity重新返回前台(active)时会继续接收到最新的数据。一句话概括,LiveData是粘性的。
  • configuration changes时,不需要额外的处理来保存数据我们知道,当你把数据存储在组件中时,当configuration
    change(比如语言、屏幕方向变化)时,组件会被recreate,然而系统并不能保证你的数据能够被恢复的。当我们采用LiveData保存数据时,因为数据和组件分离了。当组件被recreate,数据还是存在LiveData中,并不会被销毁。
  • 资源共享
    通过继承LiveData类,然后将该类定义成单例模式,在该类封装监听一些系统属性变化,然后通知LiveData的观察者。

免费获取安卓开发架构的资料(包括Fultter、高级UI、性能优化、架构师课程、 NDK、Kotlin、混合式开发(ReactNative+Weex)和一线互联网公司关于android面试的题目汇总可以加:936332305 / 链接:点击链接加入【安卓开发架构】:https://jq.qq.com/?_wv=1027&k=515xp64

LiveData的简单使用

LiveData注册的观察者的两种方式:

    // 这个方法添加的observer会受到owner生命周期的影响,在owner处于active状态时,有数据变化,会通知,    // 当owner处于inactive状态,不会通知,并且当owner的生命周期状态时DESTROYED时,自动removeObserver    public void observe (LifecycleOwner owner, Observer<? super T> observer)        // 这个方法添加的observer不存在生命周期概念,只要有数据变化,LiveData都会通知,并且不会自动remove    public void observeForever (Observer<? super T> observer)

LiveData发布修改的两种方式:

     // 这个方法必须在主线程调用        protected void setValue (T value)                // 这个方式主要用于在非主线程调用         protected void postValue (T value) LiveData

官方API文档:https://developer.android.google.cn/reference/androidx/lifecycle/

LiveData举个例子:在Activity页面有一TextView,需要展示用户User的信息,User 类定义:

    public class User {        public String name;        public int sex;            public User(String name, int sex) {            this.name = name;            this.sex = sex;        }            @Override        public String toString() {            return "User{" +                    ", name='" + name + '\'' +                    ", sex='" + sex+ '\'' +                    '}';        }    }

常规的做法:

    // 获取User的数据后    mTvUser.setText(user.toString());

这样做的一个问题,如果获取或者修改User的来源不止一处,那么需要在多个地方更新TextView,并且如果在多处UI用到了User,那么也需要在多处更新。

怎么优化这个问题呢?使用 LiveData。很多时候,LiveData与ViewModel组合使用(ViewModel后续文章会分析),让LiveData持有User 实体,作为一个被观察者,当User改变时,通知所有使用User的观察者自动change。

首先构建一个UserViewModel如下:

    public class UserViewModel extends ViewModel {            //声明userLiveData        private MutableLiveData<User> userLiveData;            //获取userLiveData        public LiveData getUserLiveData() {            if(userLiveData == null) {                userLiveData = new MutableLiveData<>();            }            return userLiveData;        }    }

然后注册观察者:

    public class TestActivity extends AppCompatActivity {            private UserViewModel mModel;        private TextView mUserTextView;            @Override        public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {            super.onCreate(savedInstanceState, persistentState);                mModel = ViewModelProviders.of(this).get(UserViewModel.class);                //创建用来更新ui的观察者,重写onChange方法,该方法会在数据发生变化时            //通过LiveData调用观察者的onChange对数据变化响应            final Observer<User> userObserver = new Observer<User>() {                @Override                public void onChanged(@Nullable User user) {                    LogUtil.i(TAG, user.toString());                    //当收到LiveData发来的新数据时,更新                    mUserTextView.setText(user.toString());                }            };                //获取监听User数据的LiveData            LiveData userLiveData = mModel.getUserLiveData();                //注册User数据观察者            userLiveData.observe(this, userObserver);        }    }

数据源发送改变的时候:

    mButton.setOnClickListener(new OnClickListener() {        @Override        public void onClick(View v) {            User user = new User("zhang san", 1)            //调用LiveData的setValue方法通知观察者数据变化            mModel.getUserLiveData().setValue(user);        }    });

这样使用到 User 的地方,UI 会自动更新,日志如下:

    com.example.cimua I/TestActivity : User{name='zhang san', sex=1}

好了,LiveData的基本使用就是这么简单~~~

LiveData更多用法可以看官方文档https://developer.android.google.cn/topic/libraries/architecture/livedata

LiveData基本使用步骤总结:

  1. 创建LiveData
  2. 创建观察者Observer
  3. 调用LiveData的observe方法将LiveData以及Observer建立起发布-订阅关系
  4. 在适当的时机调用LiveData的setValue或者postValue发布新数据通知观察者

LiveData源码分析

LiveData 是基于观察者模式构建的,所以,我们分析LiveData的源码主要可以分成两部分:

  • 订阅:即调用LiveData的observe方法注册
  • 发布:即调用LiveData的setValue或者postValue方法发布数据

LiveData的订阅流程分析

现在我们先看看observe()方法,因为observeForever()方法的实现跟observe()是类似的,我们就不看了,这里只看observe():

    // 我们已经知道,LiveData能够感知生命周期的变化。从传入的第一个参数是LifecycleOwner类型,我们已经    // 可以知道,原来LiveData是基于Lifecycle架构的基础上扩展的,我们在前一篇Lifecyle文章中已经分析过    // Lifecyle组件了,LiveData就是封装了特定的LifecycleObserver并将其注册到LifecycleOwner中,用以感知    // 生命周期    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {            // 如果LifecycleOwner的生命周期状态已经是DESTROYED,例如Activity已经destroy,        // 那么就没必要添加观察者,直接返回就可以了        if (owner.getLifecycle().getCurrentState() == DESTROYED) {            // ignore            return;        }            // 创建继承了GenericLifecycleObserver的LifecycleBoundObserver,并且将这个LifecycleBoundObserver         // 存进观察者集合mObservers中统一管理        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);        if (existing != null && !existing.isAttachedTo(owner)) {            // 同一个observer对象,只有对应的lifecycleOwner不一样,才可以重新添加,否则直接抛异常            throw new IllegalArgumentException("Cannot add the same observer"                    + " with different lifecycles");        }        if (existing != null) {            return;        }            // 调用observe()这个方法添加的observer,只有Activity/Fragment等生命周期组件可见时        // 才会收到数据更新的通知,为了知道什么时候Activity/Fragment是可见的,这里需要注册到        // Lifecycle中感知生命周期        // 也是因为这个,observe()比observeForever()多了一个参数lifecycleOwner        owner.getLifecycle().addObserver(wrapper);    }

接着我们在看看observe()方法中最重要的LifecycleBoundObserver:

    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;                // LiveData.this.mActiveCount表示处于active状态的observer的数量            // 当mActiveCount大于0时,LiveData处于active状态            // 注意区分observer的active状态和 LiveData 的active状态            boolean wasInactive = LiveData.this.mActiveCount == 0;            LiveData.this.mActiveCount += mActive ? 1 : -1;            if (wasInactive && mActive) {                // inactive -> active                onActive();            }            if (LiveData.this.mActiveCount == 0 && !mActive) {                // mActiveCount在我们修改前等于1,也就是说,LiveData从active                // 状态变到了inactive                onInactive();            }                //如果是active状态,通知观察者数据变化(dispatchingValue方法在下一节发布中分析)            if (mActive) {                // observer从inactive到active,此时客户拿到的数据可能不是最新的,这里需要dispatch                // 关于他的实现,我们下一节再看                dispatchingValue(this);            }        }    }        class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {        @NonNull final LifecycleOwner mOwner;            LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) {            super(observer);            mOwner = owner;        }            // 这个方法返回的是当前是否是active状态        @Override        boolean shouldBeActive() {            // 只有当生命周期状态是STARTED或者RESUMED时返回true,其他情况返回false            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);        }            // 这个方法是由Lifecycle结构中的mLifecycleRegistry所调用,一旦LifecycleOwner的生命周期        // 发生变化,都会调用到onStateChanged这个方法进行生命周期转换的通知        @Override        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {            // 上面我们说过,使用LiveData只需要关心注册,不需要关心何时解绑,这里就告诉我们答案:            // 当生命周期状态为DESTROYED,会自动removeObserver实现解绑,不会导致内存泄露。            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {                removeObserver(mObserver);                return;            }                // 一开始创建LifecycleBoundObserver实例的时候,mActive默认为false,            // 当注册到Lifecycle后,Lifecycle会同步生命周期状态给我们(也就是回调本方法),            // 不熟悉lifecycle的读者,可以看前一篇讲述Lifecycle的文章            activeStateChanged(shouldBeActive());        }            @Override        boolean isAttachedTo(LifecycleOwner owner) {            return mOwner == owner;        }            @Override        void detachObserver() {            mOwner.getLifecycle().removeObserver(this);        }    }

到这里,LiveData的订阅流程就基本分析完了。

LiveData的发布流程分析

上面我们已经说了,LiveData发布修改有setValue已经postValue两种方式,其中setValue只能在主线程调用,postValue则没有这个限制。

我们从setValue的分析入手发布流程:

    @MainThread    protected void setValue(T value) {        // 判断当前调用线程是否是主线程,如果不是,直接抛IllegalStateException异常        assertMainThread("setValue");        // 每次更新value,都会使mVersion + 1,ObserverWrapper也有一个字段,叫mLastVersion        // 通过比较这两个字段,可以避免重复通知观察者,还可以用于实现LiveData的粘性事件特性(后面会说到)        mVersion++;        // 将这次数据保存在LiveData的mData变量中,mData的值永远是最新的值        mData = value;        // 发布        dispatchingValue(null);    }

接着再看看 dispatchingValue方法:

    // 如果参数initiator为null的话,表示要将新数据发布通知给所有observer    // 如果参数不为null的话,表示只通知给传入的观察者initiator    private void dispatchingValue(@Nullable ObserverWrapper initiator) {        if (mDispatchingValue) {            mDispatchInvalidated = true;            return;        }            // 在observer的回调里面又触发了数据的修改        // 设置mDispatchInvalidated为true后,可以让下面的循环知道        // 数据被修改了,从而开始一轮新的迭代。        //        // 比方说,dispatchingValue -> observer.onChanged -> setValue -> dispatchingValue        // 这里return的是后面那个dispatchingValue,然后在第一个        // dispatchingValue会重新遍历所有的observer,并调用他们的onChanged。        //        // 如果想避免这种情况,可以在回调里面使用 postValue 来更新数据        mDispatchingValue = true;        do {            mDispatchInvalidated = false;            if (initiator != null) {                // 调用 observer.onChanged()                considerNotify(initiator);                initiator = null;            } else {                // initiator不为空,遍历mObservers集合,试图通知所有观察者                for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {                    considerNotify(iterator.next().getValue());                    if (mDispatchInvalidated) {                        // 某个客户在回调里面更新了数据,break后,这个for循环会重新开始                        break;                    }                }            }        } while (mDispatchInvalidated);        mDispatchingValue = false;    }

看过我那篇 lifecycle 源码分析的读者应该对 dispatchingValue 处理循环调用的方式很熟悉了。以这里为例,为了防止循环调用,我们在调用客户代码前先置位一个标志(mDispatchingValue),结束后再设为 false。如果在回调里面又触发了这个方法,可以通过 mDispatchingValue 来检测。

检测到循环调用后,再设置第二个标志(mDispatchInvalidated),然后返回。返回又会回到之前的调用,前一个调用通过检查 mDispatchInvalidated,知道数据被修改,于是开始一轮新的迭代。

接着继续看considerNotify方法:

    private void considerNotify(ObserverWrapper observer) {        // 如果observer的状态不是active,那么不向该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;        }            // 上面的源码分析,我们知道每一次setValue或者postValue的调用都会是mVersion自增1,        // mLastVersion的作用是为了与mVersion作比较,这个比较作用主要有两点:        // 1.如果说mLastVersion >= mVersion,证明这个观察者已经接受过本次发布事件通知,不需要重复通知了,直接返回        // 2.实现粘性事件。比如有一个数据(LiveData)在A页面setValue()之后,则该数据(LiveData)中的        // 全局mVersion+1,也就标志着数据版本改变,然后再从A页面打开B页面,在B页面中开始订阅该LiveData,        // 由于刚订阅的时候内部的数据版本都是从-1开始,此时内部的数据版本就和该LiveData全局的数据        // 版本mVersion不一致,根据上面的原理图,B页面打开的时候生命周期方法一执行,则会进行notify,        // 此时又同时满足页面是从不可见变为可见、数据版本不一致等条件,所以一进B页面,B页面的订阅就会被响应一次        if (observer.mLastVersion >= mVersion) {            return;        }            // 设置mLastVersion = mVersion,以免重复通知观察者        observer.mLastVersion = mVersion;            // 这里就最终调用了我们一开始通过observe()方法传入的observer中的onChange()方法        // 即新数据被发布给了observer        observer.mObserver.onChanged((T) mData);    }

setValue的分析就到此为止了~

在setValue的基础上,分析postValue就比较简单了:

    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;        }                // 通过handler将mPostValueRunnable分发到主线程执行,其实最终执行的也是setValue方法        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);    }

LiveData的源码就分析这么多了,更详细就需要到android源码里面找了。

总结

将LiveData的源码抽象为一张流程图来展示,下面的其他问题都可以在这张图中找到答案:

  • LiveData为什么能够感知生命周期?

lifecycle-aware compents的核心就是生命周期感知,要明白LiveData为什么能感知生命周期,就要知道Google的这套生命周期感知背后的原理是什么,下面是我基于之前lifeycycle这套东西刚出来时候对源码进行的一个分析总结(现在的最新代码可能和之前有点出入,但是原理上基本是一样的):

首先Activity/Fragment是LifecycleOwner(26.1.0以上的support包中Activity已经默认实现了LifecycleOwner接口),内部都会有一个LifecycleRegistry存放生命周期State、Event等。而真正核心的操作是,每个Activity/Fragment在启动时都会自动添加进来一个Headless Fragment(无界面的Fragment),由于添加进来的Fragment与Activity的生命周期是同步的,所以当Activity执行相应生命周期方法的时候,同步的也会执行Headless Fragment的生命周期方法,由于这个这个Headless Fragment对我们开发者来说是隐藏的,它会在执行自己生命周期方法的时候更新Activity的LifecycleRegistry里的生命周期State、Event, 并且notifyStateChanged来通知监听Activity生命周期的观察者。这样就到达了生命周期感知的功能,所以其实是一个隐藏的Headless Fragment来实现了监听者能感知到Activity的生命周期。

基于这套原理,只要LiveData注册了对Activity/Fragment的生命周期监听,也就拥有了感知生命周期的能力。

  • LiveData为什么可以避免内存泄露?

由于LiveData会在Activity/Fragment等具有生命周期的lifecycleOwner onDestory的时候自动解绑,所以解决了可能存在的内存泄漏问题。之前我们为了避免这个问题,一般有注册绑定的地方都要解绑,而LiveData利用生命周期感知功能解决了这一问题。

我们可以知道,当Activity/Fragment的生命周期发生改变时,LiveData中的监听都会被回调,所以避免内存泄漏就变得十分简单,可以看上图,当LiveData监听到Activity onDestory时则removeObserve,使自己与观察者自动解绑。这样就避免了内存泄漏。

  • LiveData为什么可以解决空指针异常?

我们通常在一个异步任务回来后需要更新View,而此时页面可能已经被回收,导致经常会出现View空异常,而LiveData由于具备生命周期感知功能,在界面可见的时候才会进行响应,如界面更新等,如果在界面不可见的时候发起notify,会等到界面可见的时候才进行响应更新。所以就很好的解决了空异常的问题。

  • LiveData为什么是粘性的?

所谓粘性,也就是说消息在订阅之前发布了,订阅之后依然可以接受到这个消息,像EventBus实现粘性的原理是,把发布的粘性事件暂时存在全局的集合里,之后当发生订阅的那一刻,遍历集合,将事件拿出来执行。

而LiveData之所以本身就是粘性的,结合上面的原理图我们来分析一下,比如有一个数据(LiveData)在A页面setValue()之后,则该数据(LiveData)中的全局mVersion+1,也就标志着数据版本改变,然后再从A页面打开B页面,在B页面中开始订阅该LiveData,由于刚订阅的时候内部的数据版本都是从-1开始,此时内部的数据版本就和该LiveData全局的数据版本mVersion不一致,根据上面的原理图,B页面打开的时候生命周期方法一执行,则会进行notify,此时又同时满足页面是从不可见变为可见、数据版本不一致等条件,所以一进B页面,B页面的订阅就会被响应一次。这就是所谓的粘性,A页面在发消息的时候B页面是还没创建还没订阅该数据的,但是一进入B页面一订阅,之前在A中发的消息就会被响应。

免费获取安卓开发架构的资料(包括Fultter、高级UI、性能优化、架构师课程、 NDK、Kotlin、混合式开发(ReactNative+Weex)和一线互联网公司关于android面试的题目汇总可以加:936332305 / 链接:点击链接加入【安卓开发架构】:https://jq.qq.com/?_wv=1027&k=515xp64

更多相关文章

  1. mybatisplus的坑 insert标签insert into select无参数问题的解决
  2. python起点网月票榜字体反爬案例
  3. Android(安卓)Service生命周期及用法!
  4. SharePreferences实现
  5. 我见过的最好的DataBinding解析
  6. Android(安卓)SQLite 的介绍和使用(二)
  7. Android(安卓)官方架构组件(二)——LiveData
  8. Android(安卓)开发中使用 SQLite 数据库
  9. Android(安卓)从properties配置文件读取数据

随机推荐

  1. android:layout_gravity和android:gravit
  2. android 模拟器 自定义分辨率 没有键盘
  3. Android(安卓)中的Service
  4. android adb 使用初阶
  5. 百度地图android客户端的AndroidMainfest
  6. android最佳实践(四)
  7. Android中数据存储的5种方法
  8. Android 面试题总结之Android 基础(六)
  9. Android负责人:完全开放和一致体验是挑战
  10. Android(安卓)TextView 文字空白解决方案