上一篇文章讲到了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(安卓)开发中使用 SQLite 数据库
  4. Android(安卓)从properties配置文件读取数据
  5. android filter
  6. 10天学通Android开发(7)-数据存储
  7. 用python第三方库(surface_stats_collector.py) 获取 Android(安
  8. Android(安卓)content provider基础与使用
  9. Android利用Fiddler进行网络数据抓包

随机推荐

  1. Android 自定义ImageView,支持圆角和直角
  2. 1、android 模拟小球来回撞墙效果(游戏的
  3. android 返回键 事件
  4. android打电话、发短信实现
  5. Vibrator
  6. Android在线资源API
  7. Android Activity设置无标题和全屏
  8. Android HAL 研究开发 FOR LED
  9. Android 热更新ICON图标
  10. android之通过URL来获取网络资源并下载资