Jetpack之LiveData

2,268 阅读13分钟

1.什么是LiveData

LiveData组件是Jetpack新推出基于观察者的消息订阅/分发组件,具有宿主(Activity/Fragment)生命周期感知能力,这种感知能力可确保LiveData仅分发消息给处于活跃状态的观察者,即只有处于活跃状态的观察者才能收到消息。

特点:

  1. LiveData了解UI界面的状态,如果activity不在屏幕上显示,livedata不会触发没必要的界面更新,如果activity已经被销毁,会自动清空与observer的连接,意外的调用就不会发生。
  2. LiveData是一个LifecycleOwner,他可以直接感知activity或fragment的生命周期。

活跃状态:通常情况下等于Observer所在宿主处于started、resumed状态,如果使用observeForever注册的,则一直处于活跃状态。

LiveData的消息分发机制是以往的Handler、EventBus、RxJavaBus无法比拟的,它们不会顾及当前页面是否可见一股脑的有消息就转发。导致即便应用在后台页面不可见的情况下还在做一些无用的工作抢占资源。举个例子,细心的同学可以发现微信消息列表是在页面可见状态时才会更新列表最新信息的。

LiveData的出现解决了以往使用callback回调可能带来的NPE,生命周期越界,后台任务抢占资源等问题。

从代码的角度来看一看LiveData与传统消息分发组件的不同:

class MainActivity extends AppcompactActivity{
    public void onCreate(Bundle bundle){

     Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
             //无论页面可见不可见,都会去执行页面刷新,IO。更有甚者弹出对话框
        }
      };
     //1.无论当前页面是否可见,这条消息都会被分发。----消耗资源
     //2.无论当前宿主是否还存活,这条消息都会被分发。---内存泄漏
    handler.sendMessage(msg)

    liveData.observer(this,new Observer<User>){
         void onChanged(User user){
           
         }
     }
    //1.减少资源占用---          页面不可见时不会派发消息
    //2.确保页面始终保持最新状态---页面可见时,会立刻派发最新的一条消息给所有观察者--保证页面最新状态
    //3.不再需要手动处理生命周期---避免NPE
    //4.可以打造一款不用反注册,不会内存泄漏的消息总线---取代eventbus
    liveData.postValue(data);
  }
}

2.LiveData的优势

1.确保页面符合数据状态

LiveData遵循观察者模式。当生命周期状态发生变化时,LiveData会通知Observer对象并把最新数据派发给它。观察者可以在收到onChanged事件时更新界面,而不是每次数据发生更改时立即更新界面。

2.不再需要手动处理生命周期

只需要观察相关数据。不用手动停止或回复观察。LiveData会自动管理Observer的反注册,因为它能感知宿主生命周期的变化,并在宿主生命周期的onDestory自动进行反注册。因为使用LiveData做消息分发不会发生内存泄露。

3.数据始终保持最新状态

如果宿主的生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的Activity会在返回前台后立即接收最新的数据。

4.支持黏性事件的分发

即先发送一条数据,后注册一个观察者,默认是能够收到之前发送的那条数据

5.共享资源

我们可以使用单例模式拓展LiveData,实现全局的消息分发总线

3.LiveData的使用

使用LiveData之前需要先添加依赖:

//通常情况下,只需要添加appcompat就可以了
api 'androidx.appcompat:appcompat:1.1.0'
    
//如果想单独使用,可引入下面依赖
api 'androidx.lifecycle:lifecycle-livedata:2.0.0'

LiveData的实现方式

1.MutableLiveData

我们在使用LiveData做消息分发的时候,需要使用这个子类。之所以这么设计,是考虑到单一开闭原则,只有拿到MutableLive对象才可以发送消息,Livedata对象只能接受消息,避免拿到LiveData对象时既能发消息也能收消息的混乱使用

public class MutableLiveData<T> extends LiveData<T> {
    
      //子线程中执行
      @Override
      public void postValue(T value) {
        super.postValue(value);
      }

      //主线程中执行
      @Override
      public void setValue(T value) {
          super.setValue(value);
      }
   }

2.MediatorLiveData

  • 可以统一观察多个LiveData发射的数据进行统一的处理。
  • 同时也可以做为一个LiveData,被其他Observer观察。
//创建两个长得差不多的LiveData对象
LiveData<Integer> liveData1 =  new MutableLiveData();
LiveData<Integer> liveData2 = new MutableLiveData();

 //再创建一个聚合类MediatorLiveData
 MediatorLiveData<Integer> liveDataMerger = new MediatorLiveData<>();
 //分别把上面创建LiveData 添加进来。
liveDataMerger.addSource(liveData1, observer);
liveDataMerger.addSource(liveData2, observer);

Observer observer = new Observer<Integer>() {
  @Override
 public void onChanged(@Nullable Integer s) {
      titleTextView.setText(s);
 }
//一旦liveData1或liveData2发送了新的数据 ,observer便能观察的到,以便统一处理更新UI

基本使用

定义LifeData,项目中Livedata一般都存放在ViewModel中,以保证app配置变更时,数据不会丢失。

1.定义观察者,用以观察livedata中的数据变化

//需要一个观察者来观察数据
Observer observer=new Observer<String>(){
        @Override
        public void onChanged(String s) {
            nameTextView.setText(s);
        }
};

2.Livedata订阅observer

public class NameViewModel extends ViewModel{
    
    LiveData<String> currentName =  new MutableLiveData();
    
    public LiveData<String> getCurrentName(){
        return currentName;
    }
}

//获取到viewmodel
model= ViewModelProviders.of(this).get(NameViewModel.class);
//取出livedata完成订阅
model.getCurrentName().observe(this,observer);

3.Livedata发送消息,通知observer更新数据

//会回调observer中的onChanged方法
model.getCurrentName().setValue(anotherName);

Transformations.map 操作符

可以对LiveData的数据进行变化,并且返回一个新的LiveData对象,这一点了解即可。

MutableLiveData<Integer> data = new MutableLiveData<>();

//数据转换
LiveData<String> transformData = Transformations.map(data, input ->   String.valueOf(input));
//使用转换后生成的transformData去观察数据
transformData.observe( this, output -> {

});

//使用原始的livedata发送数据
data.setValue(10);

LiveData核心方法

方法名作用
observe(LifecycleOwner owner,Observer observer)注册和宿主生命周期相关的观察者
observeForever(Observer observer)注册观察者,不会反注册,需自行维护
setValue(T data)发送数据,没有活跃的观察者时不分发,只能在主线程
postValue(T data)和setValue一样,不受线程环境限制
onActive当且仅当有一个活跃的观察者时才触发
inActivie不存在活跃的观察者时才触发

4.LiveData实现原理

消息分发的流程如下图:

注册观察者时触发的消息分发流程

注册监听

observe注册时,可以主动跟宿主生命周期绑定,不用反注册

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
        //1. 首先来个断言,这个方法只能在主线程调用,observeForever也是。
        assertMainThread("observe");
    
        //2.其次把注册进来的observer包装成一个具有生命周边边界的观察者
        //它能监听宿主被销毁的事件,从而主动的把自己反注册,避免内存泄漏
        //此时观察者是否处于活跃状态就等于宿主是否可见
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    
        //3.接着会判断该观察是否已经注册过了,如果是则抛异常,所以要注意,不允许重复注册
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        //4.这一步才是关键
        //利用Lifecycle,把观察者注册到进去,才能监听到宿主生命周期状态的变化,对不对?
        //根据上节的分析,一旦一个新的观察者被添加,Lifecycle也会同步它的状态和宿主一致对不对?此时会触发观察者的onStateChanged方法
        owner.getLifecycle().addObserver(wrapper);
    }

LifecycleBoundObserver监听宿主的生命周期,并且宿主不可见时不分发任何数据

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
            super(observer);
        }

        @Override
        boolean shouldBeActive() {
        //使用observer方法注册的观察者都会被包装成LifecycleBoundObserver
        //观察者是否活跃就等于宿主 的状态是否大于等于STARTED,
        //如果页面当前不可见,你发送了一条消息,此时是不会被分发的,可以避免后台任务抢占资源,当页面恢复可见才会分发。
        //注意:如果使用observerForever注册的观察者,
        //会被包装成AlwaysActiveObserver,它的shouldBeActive一致返回true.即便在页面不可见也能收到数据
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }
    
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
                //在这里如果监听到宿主被销毁了,则主动地把自己从livedata的观察者中移除掉
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);
                return;
            }
            //否则说明宿主的状态发生了变化,此时会判断宿主是否处于活跃状态
            activeStateChanged(shouldBeActive());
        }
    }

数据状态变更

ObserverWarpper状态变更后,如果观察者处于活跃状态会触发数据的分发流程:

abstract class ObserverWrapper{
        final Observer<? super T> mObserver;
        boolean mActive;
        int mLastVersion = START_VERSION//这里就等于-1,没有主动和LiveData的mVersion对齐,为黏性事件埋下了伏笔
        
        void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
            //更改观察者的状态
            mActive = newActive;
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            //如果此时有且只有一个活跃的观察者则触发onActive
            LiveData.this.mActiveCount += mActive ? 1 : -1;
            if (wasInactive && mActive) {
                onActive();
            }
            //没有任何一个活跃的观察者则触发onInactive
            //利用这个方法被触发的时机,可以做很多事,比如懒加载,资源释放等
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                onInactive();
            }
            //如果此时观察者处于活跃状态,下面就开始分发数据了
            //请注意,这里传递了this = observer
            if (mActive) {
                dispatchingValue(this);
            }
        }
} 

数据分发

dispatchingValue数据分发流程控制

void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
            //如果传递的观察者不为空,则把数据分发给他自己。这个流程是新注册观察者的时候会被触发
                considerNotify(initiator);
                initiator = null;
            } else {
                //否则遍历集合中所有已注册的的观察者,逐个调用considerNotify,分发数据
                for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }

considerNotity数据真正分发的地方,需要满足三个条件:

private void considerNotify(ObserverWrapper observer) {
        //1.观察者当前状态不活跃不分发
        if (!observer.mActive) {
            return;
        }
        //2.观察者所在宿主是否处于活跃状态,否则不分发,并且更改观察者的状态为false
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        //3.此处判断观察者接收消息的次数是否大于等于发送消息的次数
        //但是observer被创建之初verison=-1 
        //如果此时LiveData已经发送过数据了。这里就不满足了,就出现黏性事件了,后注册的观察者收到了前面发送的消息。
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        //每分发一次消息,则把观察者和LiveData的version对齐,防止重复发送
        observer.mLastVersion = mVersion;
        //最后的数据传递
        observer.mObserver.onChanged((T) mData);
    }

问题:黏性消息

即新注册的observer也能接受到前面发送的最后一条数据。原因就在于LiveData每次发送一条数据它的mVersion都会+1。但是新注册的Observer的lastVersion = 0 ,图中的considerNotity方法就会把前面发送的数据分发给信注册的Observer了。

普通消息分发流程

即调用postValue,setValue才会触发消息的分发:

5.observerForever

我们经常会使用observer(),observerForever()去注册观察者,它俩有什么区别呢?

  • observer():不需要手动反注册,并且宿主不可见时收不到消息,当宿主回复可见时,会立刻受到最新的数据;
  • observeForever():需要自行手动反注册,并且无论宿主是否可见,都能够收到消息;
  • 可以充分利用onActive()方法被激活的时机,来实现一些数据懒加载的功能。

6.LiveDataBus

既然我们已经知道了LiveData产生黏性事件(如果之前发送的时间,而后注册的观察者也能接受到这条消息)的原因?那么如何去解决呢?

LiveDataBus和EventBus的使用方式非常相像,但我们的优势不用手动反注册,不用担心内存泄露了。

LiveDataBus.with("eventName").observer(lifecycleOwner,sticky,new Observer<String>{
   void onChanged(String data){
   
   }
})

核心思想

在LiveData的消息分发核心方法:

void considerNotify(ObserverWrapper observer) {
      //观察者没有处于活跃状态,则不分发。
       if (!observer.shouldBeActive()) {
           observer.activeStateChanged(false);
           return;
       }
       //观察者接收的消息的次数>=livedata发送消息的次数,不分发。
       //如果之前已经发送过数据了,新注册的observer也能接收到最后一条数据。
       if (observer.mLastVersion >= mVersion) {
           return;
       }
  
      //根本原因在于ObserverWrapper的version字段在创建时=-1,没有主动和LiveData的mVersion字段对齐
       observer.mLastVersion = mVersion;
       observer.mObserver.onChanged((T) mData);
}

控制黏性事件的突破口在于观察者的version字段,我们要在注册一个新的Observer时把它的mLastVersion字段和LiveData.mVersion字段主动保持一致就可以了。

但是上面这个version字段我们都是拿不到也无法修改的,网络上有种方案使用反射强行让Observer的mLastVersion和LiveData.mVersion对齐,但是不够优雅。

所以我们的做法是使用代理设计模式,从而修改掉新注册Observer的行为,下面来看代码:

class StickyObserver<T> implements Observer<T> {
       private StickyLiveData<T> mLiveData;
       private Observer<T> mObserver;
       private boolean mSticky;

      //标记该observer已经接收几次数据了,用以过滤老数据重复接收
      private int mLastVersion = 0;

      public StickyObserver(StickyLiveData liveData, Observer<T> observer, boolean sticky) {
             //比如先使用StickyLiveData发送了一条数据。StickyLiveData#version=1
             //那当我们创建WrapperObserver注册进去的时候
             //需要把它的version和 StickyLiveData的version保持一致
             //用以过滤老数据,否则 岂不是会收到老的数据?
               mLastVersion = mLiveData.mVersion;
            }

      @Override
      public void onChanged(T t) {
              
                if (mLastVersion >= mLiveData.mVersion) {
                    //但如果当前observer它是关心 黏性事件的,则给他。
                    if (mSticky && mLiveData.mStickyData != null) {
                        mObserver.onChanged(mLiveData.mStickyData);
                    }
                    return;
                }

                mLastVersion = mLiveData.mVersion;
                mObserver.onChanged(t);
      }
}

支持黏性事件订阅、分发的StickyLiveData,自己管控消息分发的时机,相比于网络盛传的使用反射修改LiveData.mVersion字段的方式优雅的太多了

public static class StickyLiveData<T> extends LiveData<T> {
      private String mEventName;
      private T mStickyData;
      private int mVersion = 0;
      public StickyLiveData(String eventName) {
          mEventName = eventName;
      }

      @Override
      public void setValue(T value) {
          //每次发送消息,版本号就需要+1,因为我们需要通过这个version管控,要不要分发黏性事件。
          mVersion++;
          super.setValue(value);
      }

      @Override
      public void postValue(T value) {
          super.postValue(value);
      }

      public void setStickyData(T stickyData) {
          //同步的方式发送黏性消息
          this.mStickyData = stickyData;
          setValue(stickyData);
      }

      public void postStickyData(T stickyData) {
          //异步的形式发送消息
          this.mStickyData = stickyData;
          postValue(stickyData);
      }

      @Override
      public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
          observerSticky(owner, observer, false);
      }

      public void observerSticky(LifecycleOwner owner, Observer<? super T> observer, boolean sticky) {
          super.observe(owner, new WrapperObserver(this, observer, sticky));
          owner.getLifecycle().addObserver(new LifecycleEventObserver() {
              @Override
              public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) {
                  if (event == Lifecycle.Event.ON_DESTROY) {
                      //自动反注册
                      mHashMap.remove(mEventName);
                  }
              }
          });
      }
}

LiveDataBus完整代码

object LiveDataBus {

    private val eventMap = ConcurrentHashMap<String, StickyLiveData<*>>()
    fun <T> with(eventName: String): StickyLiveData<T> {
        //基于事件名称 订阅、分发消息,
        //由于 一个 livedata 只能发送 一种数据类型
        //所以 不同的event事件,需要使用不同的livedata实例 去分发
        var liveData = eventMap[eventName]
        if (liveData == null) {
            liveData = StickyLiveData<T>(eventName)
            eventMap[eventName] = liveData
        }
        return liveData as StickyLiveData<T>
    }

    //通过一堆的反射,获取livedata当中的mversion字段,来控制黏性数据的分发与否,但是我们认为这种反射不够优雅。
    class StickyLiveData<T>(private val eventName: String) : LiveData<T>() {
        internal var mStickyData: T? = null
        internal var mVersion = 0

        fun setStickyData(stickyData: T) {
            mStickyData = stickyData
            setValue(stickyData)
            //就是在主线程去发送数据
        }

        fun postStickyData(stickyData: T) {
            mStickyData = stickyData
            postValue(stickyData)
            //不受线程的限制
        }

        override fun setValue(value: T) {
            mVersion++
            super.setValue(value)
        }

        override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
            observerSticky(owner, false, observer)
        }

        fun observerSticky(owner: LifecycleOwner, sticky: Boolean, observer: Observer<in T>) {
            //允许指定注册的观察则 是否需要关心黏性事件
            //sticky =true, 如果之前存在已经发送的数据,那么这个observer会受到之前的黏性事件消息
            owner.lifecycle.addObserver(LifecycleEventObserver { source, event ->
                //监听 宿主 发生销毁事件,主动把livedata 移除掉。
                if (event == Lifecycle.Event.ON_DESTROY) {
                    eventMap.remove(eventName)
                }
            })
            super.observe(owner, StickyObserver(this, sticky, observer))
        }
    }

    class StickyObserver<T>(
        val stickyLiveData: StickyLiveData<T>,
        val sticky: Boolean,
        val observer: Observer<in T>
    ) : Observer<T> {
        //lastVersion 和livedata的version 对齐的原因,就是为控制黏性事件的分发。
        //sticky 不等于true , 只能接收到注册之后发送的消息,如果要接收黏性事件,则sticky需要传递为true
        private var lastVersion = stickyLiveData.mVersion
        override fun onChanged(t: T) {

            if (lastVersion >= stickyLiveData.mVersion) {
                //就说明stickyLiveData  没有更新的数据需要发送。
                if (sticky && stickyLiveData.mStickyData != null) {
                    observer.onChanged(stickyLiveData.mStickyData)
                }
                return
            }

            lastVersion = stickyLiveData.mVersion
            observer.onChanged(t)
        }

    }
}