一点点入坑JetPack:LiveData篇

作者: 咸鱼正翻身 | 来源:发表于2019-02-23 17:13 被阅读6次

    前言

    结束了Lifecycle篇和ViewModel篇,终于到了相对复杂的LiveData篇了。

    最开始了解LiveData我是拒绝的,因为你不能上来就让我用,马上就用。第一我要试一下,我不原意用完以后...duang、duang都是bug....

    一点点入坑JetPack:ViewModel篇

    一点点入坑JetPack:Lifecycle篇

    后来用完之后,好嗨呦,感觉人生已经达到了高潮...

    正文

    当然不想听我瞎bb的,可以直接官方文档。如果想图个乐,顺便了解了解新技术。那欢迎光临红...,男宾一位,里边请!

    一、概况

    官网:LiveData是一个可观察的数据持有者类。与常规observable不同,LiveData是生命周期感知的。

    从官方文档上我们可以看到俩个关键词:可观察生命周期感知。简单来说,Google给我们提供了一个可以被观察的,并且拥有生命周期感知能力的类。那有什么用呢?

    直接上demo:

    二、入门

    1.1、初级官方demo

    class NameViewModel : ViewModel() {
        // 这里new了一个MutableLiveData,它是LiveData的实现类,LiveData是抽象的,很明显不能被new
        val currentName: LiveData<String> by lazy {
            MutableLiveData<String>()
        }
    }
    
    class NameActivity : AppCompatActivity() {
        private lateinit var mModel: NameViewModel
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            mModel = ViewModelProviders.of(this).get(NameViewModel::class.java)
            // 这个this是LifecycleOwner
            mModel.currentName.observe(this, Observer { newName ->
                // mNameTextView一个TextView
                mNameTextView.text = newName
            })
            // 更新被观察者数据,LiveData会通知观察者
            mModel.currentName.postValue("MDove")
        }
    }
    

    如果看到这几行代码,豁然开朗,那么可以跳过看下面的部分。如果感觉有些疑惑,不着急咱们不打针不吃药,坐下就是和你唠...

    1.2、demo解释

    最开始我们先声明了一个NameViewModel的ViewModel,这部分内容在ViewModel篇有所提及。内部有一个MutableLiveData<String>的成员变量。说白了就是一个LiveData类型的String,我们使用时是借助LiveData的特性,但本质还是用String。

    也就是说这里如果我们要用一个List<String>,那么此时就是MutableLiveData<List<String>>()

    Activity之中,我们先获取ViewModel,然后mModel.currentName.observe(...,...),这里我们就是在观察LiveData。我们只需要在回调中处理我们自己的UI操作即可了。也就是demo中的mNameTextView.text = newName

    LiveData会在每一次postValue(...)或者value=...时,observe()便会回调,哪怕是null。

    注意

    这里有俩个点需要特别注意:

    • 1、LiveData是生命周期感知的,在当前的LifecycleOwner不处于活动状态(例如onPasue()onStop())时,LiveData是不会回调observe()的,因为没有意义。
    • 2、如果LiveData没有被observe(),那么此时你调用这个LiveData的postValue(...)/value=...,是没有任何作用。这个我们可以在源码中看到。

    1.3、不同的LiveData实现类(系统实现)

    MutableLiveData

    上文咱们已经见过了,没什么特别的,就是LiveData的实现类。就相当于List和ArrayList的关系。

    MediatorLiveData

    MutableLiveData的子类,它是一个比较强大的LiveData,我们的map()switchMap()都是基于它进行实现的。
    最大的特点是可以同时监听多个LiveData。

    三、进阶

    官网的这个小破demo,属实太寒酸了,你倒是加点特技啊?就这中初级用法,谁能觉得好用呢!所以,如果对LiveData稍稍有点感觉,那咱们不要停,一起决战到天明。

    3.1、map()

    初用过RxJava的小伙伴,估计会和我一样,被各种“姿势”的操作符所正经,比如常用的:map、flatMap...而LiveData中同样有这样的操作。

    一个很常见的场景:我们通过一个唯一id去查询这个id下的实体类,并且要同时展示二者的数据。很简单的业务逻辑,在LiveData中的展示是这样的:

    3.1.1、使用

    class NameViewModel : ViewModel() {
        val userIdLiveData = MutableLiveData<Long>()
        // 伪码:当userIdLiveData发生变化时,userLiveData中的map就会调用,那么我们就可以得到罪行的id
        val userLiveData: LiveData<User> = Transformations.map(userIdLiveData) { id->
            // 通过id拿到User,return一个User的实例user
            user
        }
    }
    
    // Activity中
    mModel.userLiveData.observe(this, Observer { user ->
        // user变化后通知mNameTextView更新UI
        mNameTextView.text = user.name
    })
    // 给userIdLiveData设置id为1
    mModel.userIdLiveData.postValue("1")
    

    针对这个业务场景,我们只需要监听我们用户通知UI变化的LiveData(userLiveData),然后通过userIdLiveData.postValue("1")来驱动数据的变化。

    这里可能和我们传统的MVP的思想并不相同,毕竟MVVM和MVP还是有区别的,而MVVM的这种方式被称之为:数据驱动

    3.1.2、map()源码

    我们直接点到Transformations.map()中。

    @MainThread
    public static <X, Y> LiveData<Y> map(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, Y> mapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(mapFunction.apply(x));
            }
        });
        return result;
    }
    

    很简单,就是使用了MediatorLiveData,然后通过一个高阶函数,将高阶函数返回的内容,set到LiveData上,完成map()。

    既然提到了MediatorLiveData,以及它的addSource()的方法,那么我们就来看看它的源码。

    3.1.3、MediatorLiveData源码

    这部分没啥意思,可以直接跳过看3.1.4、map()源码总结...

    进入MediatorLiveData之中,我们会发现代码比较少。这里抽出俩块比较重点的内容,我们一同来感受一下:

     @MainThread
    public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
        Source<S> e = new Source<>(source, onChanged);
        Source<?> existing = mSources.putIfAbsent(source, e);
        if (existing != null && existing.mObserver != onChanged) {
            throw new IllegalArgumentException(
                    "This source was already added with the different observer");
        }
        if (existing != null) {
            return;
        }
        if (hasActiveObservers()) {
            e.plug();
        }
    }
    

    从这段代码中,我们粗略可以得到一个信息,这里把我们的LiveData和Observer封装成了Source对象,并且这个对象,不能重复添加。

    此外,Source的plug()方法,被调用。接下来我们去看一看这个内部类Source的实现:

    private static class Source<V> implements Observer<V> {
        final LiveData<V> mLiveData;
        final Observer<? super V> mObserver;
        int mVersion = START_VERSION;
    
        Source(LiveData<V> liveData, final Observer<? super V> observer) {
            mLiveData = liveData;
            mObserver = observer;
        }
    
        void plug() {
            mLiveData.observeForever(this);
        }
    
        void unplug() {
            mLiveData.removeObserver(this);
        }
    
        @Override
        public void onChanged(@Nullable V v) {
            if (mVersion != mLiveData.getVersion()) {
                mVersion = mLiveData.getVersion();
                mObserver.onChanged(v);
            }
        }
    }
    

    首先Source是一个观察者,可以看到,我们外部使用的Observer会以Source的成员变量的形式,添加到传入的LiveData中。值得注意的是,这里使用了mLiveData.observeForever(this);

    observeForever()用法可以看到,我们并没有传递LifecycleOwner,因此它并不具备生命感知能力。从注释中也可见一斑:This means that the given observer will receive all events and will never be automatically removed.

    3.1.4、map()源码总结

    打住,打住吧。其实没必要继续看了。一句话总结:map()的原理就是基于MediatorLiveData,MediatorLiveData内部会将传递进来的LiveData和Observer封装成内部类,然后放在内部维护的一个Map中。并且自动帮我们完成observeForever()和removeObserver()

    3.2、switchMap()

    3.2.1、使用

    switchMap()的场景可以应用在切换LiveData上。这话怎么解释呢?

    很常见的业务场景:比如你的业务用的是map(),map()中使用你自己写的络,而且LiveData运行的很良好,抽着烟喝着酒,啥事都没有...就比如,上面map()那样的代码:

    val userLiveData: LiveData<User> = Transformations.map(userIdLiveData) { id->
        // 自己的一段逻辑
        user
    }
    // Activity中
    mViewModel.userLiveData.observe(this,Observer{->user
        //更新UI
    })
    

    突然有一天,这个地方数据结构、UI都没变,唯独变了逻辑。此时你一个同事写好了一个方法,让你替换一下就好了了。不过当你调用的时候突然返现,这个方法返回一个LiveData对象!

    当然此时我们可以让UI重新observe()这个LiveData对象:

    val otherLiveData:LiveData<User> = // 同事的逻辑
    
    // Activity中重新observe()
    mViewModel.otherLiveData.observe(this,Observer{->user
        //更新UI
    })
    

    可是这样的话,自己之前写的东西不都白费了么?所以此时,我们可以使用switchMap(),我们只需要很少的改动,就可以设配这次需求的变动:

    val userLiveData: LiveData<User> = Transformations.switchMap(userIdLiveData) { id->
        // 直接把同事的代码放在这里即可
    }
    

    3.2.2、switchMap()源码

    有了上边map()源码基础,我们可以很容易的看出switchMap()的端倪:

    @MainThread
    public static <X, Y> LiveData<Y> switchMap(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, LiveData<Y>> switchMapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            LiveData<Y> mSource;
    
            @Override
            public void onChanged(@Nullable X x) {
                // 从Function中拿到返回的LiveData,也就是我们新的LiveData(文章背景中同事写的LiveData)
                LiveData<Y> newLiveData = switchMapFunction.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                // remove掉旧的LiveData
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    // add新的LiveData
                    result.addSource(mSource, new Observer<Y>() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            // 通知LiveData发生变化
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }
    

    我们对比一下switchMap()map()的参数类型:

    • Function<X, LiveData<Y>> switchMapFunction
    • Function<X, Y> mapFunction

    很明显一个是返回LiveData类型,一个是换一种类型。这也说明了,这俩个方法的不一样之处

    代码解释可以看注释,很直白的思路。

    3.3、MediatorLiveData的使用

    上述我们看过了map()switchMap()的用法。各位应该都注意到MediatorLiveData这个类的作用。

    上边我们一直都在操作一个LiveData,但是我们需求很容易遇到多种状态的变化。就像官方的demo:

     LiveData liveData1 = ...;
     LiveData liveData2 = ...;
    
     MediatorLiveData liveDataMerger = new MediatorLiveData<>();
     liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
     liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
    

    如同demo所示,我们可以同时add多个LiveData,根据不同的LiveData的变化,处理我们不同的逻辑。最后通过MediatorLiveData回调到我们的UI上。

    四、源码分析

    注册Observer

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        // 如果当前生命周期是DESTROYED,直接return
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            return;
        }
        // 这个包装类,做了一件事情,在DESTROYED,移除Observer
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        // 添加在已经Observer,已存在且在Attach上后直接抛异常,也就是不能重复add
        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;
        }
        // 把Wrapper添加到LifecycleOwner上
        owner.getLifecycle().addObserver(wrapper);
    }
    

    Observer如何被响应:

    public interface Observer<T> {
        /**
         * Called when the data is changed.
         * @param t  The new data
         */
        void onChanged(T t);
    }
    

    触发的起点,很明显是我们在set/postValue的时候:

    @MainThread
    protected void setValue(T value) {
        // 记住这个值,它是用来表示数据是否发生变化的
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }
    
    void dispatchingValue(@Nullable ObserverWrapper initiator) {
        // 省略部分代码 
        for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
            mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
            // 往里走
            considerNotify(iterator.next().getValue());
            if (mDispatchInvalidated) {
                break;
            }
        }
        // 省略部分代码
    }
    
    private void considerNotify(ObserverWrapper observer) {
        // 如果observer不在活动期,则直接return。也就是上述说observer不在前台,将不会接受回调。
        if (!observer.mActive) {
            return;
        }
        // 省略部分代码
        // 很直白的version对比
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        // 回调
        observer.mObserver.onChanged((T) mData);
    }
    

    observer.mActive在哪被赋值?很多地方。除了一些边界条件的赋值外,一个比较正式的赋值,ObserverWrapper中的void activeStateChanged(boolean newActive)方法:

    void activeStateChanged(boolean newActive) {
        if (newActive == mActive) {
            return;
        }
        mActive = newActive;
    }
    // 此方法最终会调到此方法
    @Override
    boolean shouldBeActive() {
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }
    public boolean isAtLeast(@NonNull State state) {
        return compareTo(state) >= 0;
    }
    

    很简单,每次生命周期回调,observer.mActive都会被赋值,而只有到Lifecycle是活动状态是,mActive才会true。因此只有在我们的Activity为前台时我们的LiveData才会被回调。

    尾声

    到此关于LiveData的部分就整完了,不知道各位看官是否感受到LiveData的好用之处了呢?如果没有,不如自己写一写,用身体去感受来自LiveData的爽快~

    个人公众号:咸鱼正翻身

    相关文章

      网友评论

        本文标题:一点点入坑JetPack:LiveData篇

        本文链接:https://www.haomeiwen.com/subject/vgmpyqtx.html