0、前言

如果不是很了解 LiveDataLifecycle 的同学可以先看一下我之前的文章 基于 Android Architecture Components 的 MVVM 浅析。同时安利下自己平时用的工具库 LiveDataUtils,还在不断完善中,欢迎大家 star、fork、关注和批评指正。

1、为什么要把 LiveData 当作事件传递用

利用观察者模式打造的事件总线的优点不必多说(当然也有很多缺点),如 EventBusRxBus 用的好的话能起到很好的解耦作用,使整个程序架构更加清晰,不至于到处传递各种 Callback。但是他们都缺少了对 View 层(ActivityFragment 等)的生命周期的感知能力,需要在生命周期结束时手动解除观察者,手动管理生命周期十分繁琐且很容易出错。
而 Google 推出的 Lifecycle 库就是为了解决这一问题,其中的 LiveData 就是一个具有生命周期感知能力的观察者,如果用它来打造一个能够感知生命周期的事件总线,岂不美哉!

2、LiveData 当作事件传递用的那些坑

在随着对 LiveData 的运用和理解的逐渐深入,特别是对它的「生命周期感知能力」有了更深的理解,慢慢发现这样用的一些坑,借此机会就跟大家分享探讨一下。而且我平时也有把 LiveData 纯粹当作事件传递来用,特别是列表操作(比如涉及 IO 的增删操作,View 层需要知道哪个数据改动了,以及操作是否成功等,只能以事件的形式传递)。

2.1、postValue 数据丢失的问题

在我的前一篇文章中也提到过,大家也可以直接看源码。postValue 只是把传进来的数据先存到 mPendingData,然后往主线程抛一个 Runnable,在这个 Runnable 里面再调用 setValue 来把存起来的值真正设置上去,并回调观察者们。而如果在这个 Runnable 执行前多次 postValue,其实只是改变暂存的值 mPendingData,并不会再次抛另一个 Runnable。这就会出现后设置的值把前面的值覆盖掉的问题,会导致事件丢失。

protected void postValue(T value) {boolean postTask;synchronized (mDataLock) {postTask = mPendingData == NOT_SET;// 这里先把数据暂存起来,后来的数据会覆盖前面的mPendingData = value;}// 这里保证只抛一个 mPostValueRunnable,#-.-if (!postTask) {return;}ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
复制代码

2.2、setValue 不回调观察者

LiveData 的生命周期感知能力就体现在这里,它不会回调处于「非激活状态」(即 onStart 之后到 onPause 之前)的观察者,因为这时更新 View 没有意义,而且比较危险,它会等到观察者激活之后再把新的值回调给他。
但是如果我传了多个数据(假设都是用 setValue 保证不会被覆盖),那些处于非激活状态的观察者是毫不知情的,他们在激活的时候只会收到最后一个数据。这对于事件传递来说,就表现为事件丢失,中间传的任何数据都无法收到,那也就失去了事件传递的意义。

// LiveData 通知观察者时调的函数
private void considerNotify(ObserverWrapper 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;}// 每次更新数据都会版本加一,这里保证不会回调之前发送过的数据if (observer.mLastVersion >= mVersion) {return;}observer.mLastVersion = mVersion;//noinspection uncheckedobserver.mObserver.onChanged((T) mData);
}
复制代码

2.3、LiveData 就不是为传递事件准备的

从上面两点也可以看出,LiveData (或者说它的观察者) 在观察者激活之前并不关心中间经历过多少次数据变更,它只会在某个观察者激活时,传递给他最新的值,中间的值都不会起作用。
当然 LiveData 的设计也不是为了传递事件的,它是为了反应 View 当前状态的,View 层只需要根据当前 LiveData 的值去渲染数据就行,非激活状态时 View 都不可见,就算更新了也没意义。
我最开始也是觉得 LiveData 用到了观察者模式,而且可以进行一些不同 Fragment 之间数据通讯,就想到了事件总线,现在想想当时还是 too young too naive。

当然,也不是说 LiveData 也不是没救了,有谷歌给我们提供了强大的 Lifecycle 库,我们完全可以自己造一个不会丢事件的 LiveData

3、打造一个不会丢事件的 LiveData

LiveData 的其他功能做的很完善,只是会丢事件,我们要改造就要就针对上面的问题逐个击破。

3.1、postValue 的问题

对于 postValue 的问题,既然它最后也是调用的 setValue,丢数据是因为只抛了一次 Runable,那我们就自己每次都往主线程抛一个 Runable 就能解决这个问题 具体实现可以参考之前提到的 LiveDataUtils。

/*** LiveData 相关的工具类,简化 LiveData 操作** @author funnywolf* @since 2019-04-22*/
public class LiveDataUtils {private static Handler sMainHandler;/*** 用 setValue 更新 MutableLiveData 的数据,如果在子线程,就切换到主线程*/public static <T> void setValue(MutableLiveData<T> mld, T d) {if (mld == null) {return;}if (Thread.currentThread() == Looper.getMainLooper().getThread()) {mld.setValue(d);} else {postSetValue(mld, d);}}/*** 向主线程的 handler 抛 SetValueRunnable*/public static <T> void postSetValue(MutableLiveData<T> mld, T d) {if (sMainHandler == null) {sMainHandler = new Handler(Looper.getMainLooper());}sMainHandler.post(SetValueRunnable.create(mld, d));}private static class SetValueRunnable<T> implements Runnable {private final MutableLiveData<T> liveData;private final T data;private SetValueRunnable(@NonNull MutableLiveData<T> liveData, T data) {this.liveData = liveData;this.data = data;}@Overridepublic void run() {liveData.setValue(data);}public static <T> SetValueRunnable<T> create(@NonNull MutableLiveData<T> liveData, T data) {return new SetValueRunnable<>(liveData, data);}}
}
复制代码

3.2、非激活状态的问题

其实我觉得这个问题的主要「责任」并不在 LiveData,而是在它的观察者,「是你告诉我你非激活的呀,那我怎么给你发数据呢,我发给你,万一你出问题了呢,那到底谁负责?」。
我们常用的观察者其实是 LifecycleBoundObserver,在调用 public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) 会自动帮我们封装一个这样的观察者,而它会根据 LifecycleOwner 的生命周期呈现出「激活」和「非激活」状态。

// LifecycleBoundObserver
boolean shouldBeActive() {return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
复制代码

LiveData 默认的还有另外一种观察者 AlwaysActiveObserver,它是我们在调用 public void observeForever(@NonNull Observer<? super T> observer) 时生成的,顾名思义它会一直处于激活状态,LiveData 当然也不会替我们管理这样观察者的生命周期,我们需要在不使用时手动调用 public void removeObserver(@NonNull final Observer<? super T> observer) 移除观察者,否则可能会内存泄漏。

// AlwaysActiveObserver
boolean shouldBeActive() {return true;
}
复制代码

这个 AlwaysActiveObserver 看样子能够解决我们的问题,他一直处于激活状态,那所有的事件都会回调给他,但是需要我们自己管理生命周期。这不是开历史倒车吗?好不容易有生命周期感知了,现在又要自己手动搞?

3.3、造一个生命周期感知的还不丢事件的观察者

手动管理生命周期是绝对不能忍的,AlwaysActiveObserver 可以解决刚才说的问题,那我们就造一个新的观察者来管理 observeForeverremoveObserver 的问题。既然要造,那就造个好用的,首先事件一定不能丢,要不就没意义了;而且生命周期要观察者自己管理,不能只是简单的 observeForeverremoveObserver,非激活状态之类的也要考虑进去。
既然要管理生命周期,那就不得不用到 LifecycleOwnerLifecycle,然后自己观察 LifecycleOwnerLifecycle

/*** Marks a class as a LifecycleObserver. It does not have any methods, instead, relies on* {@link OnLifecycleEvent} annotated methods.* <p>* @see Lifecycle Lifecycle - for samples and usage patterns.*/
@SuppressWarnings("WeakerAccess")
public interface LifecycleObserver {}
复制代码

Lifecycle 对外只给了这个接口,并不含有任何回调,我们需要用注释里说的 OnLifecycleEvent 注解来标记相应的函数,Lifecycle 会通过反射拿到标记的函数,然后生成对应的适配器,感兴趣的可以看下 Lifecycling.getCallback 函数。比如我们可以这样用

public class Observer implements LifecycleObserver {@OnLifecycleEvent(Lifecycle.Event.ON_START)private void onStart() {doSomethingOnStart();}
}
复制代码

拿到生命周期后,我们就可以在一开始 observeForever,在 Lifecycle.Event.ON_DESTROYremoveObserver
接下来就要考虑激活和非激活的状态了,既然用了 observeForever,那每次事件都会有回调,这时候如果 Lifecycle 处于激活状态,那可以直接把事件发出去。但如果非激活,不能直接把事件发出去,又不能丢,那我们就需要先把事件存起来,然后在 Lifecycle 变为激活状态时再把存起来的事件发送出去。简单画了下流程图。

/*** LiveData 用作事件传递时的观察者* 保证所有事件不丢失,保存非激活状态的事件,并能够在激活状态回调,且没有内存泄漏** @see AsEventBus** @author funnywolf* @since 2019-05-18*/
public class LiveEventObserver<T> implements LifecycleObserver, Observer<T> {private LiveData<T> mLiveData;private LifecycleOwner mOwner;private Observer<? super T> mObserver;private final List<T> mPendingData = new ArrayList<>();public LiveEventObserver(LiveData<T> liveData, LifecycleOwner owner, Observer<? super T> observer) {mLiveData = liveData;mOwner = owner;mObserver = observer;mOwner.getLifecycle().addObserver(this);mLiveData.observeForever(this);}/*** 在生命周期结束前的任何时候都可能会调用*/@Overridepublic void onChanged(@Nullable T t) {if (isActive()) {// 如果是激活状态,就直接更新mObserver.onChanged(t);} else {// 非激活状态先把数据存起来mPendingData.add(t);}}/*** @return 是否是激活状态,即 onStart 之后到 onPause 之前*/private boolean isActive() {return mOwner.getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED);}/*** onStart 之后就是激活状态了,如果之前存的有数据,就发送出去*/@OnLifecycleEvent(Lifecycle.Event.ON_ANY)private void onEvent(LifecycleOwner owner, Lifecycle.Event event) {if (owner != mOwner) {return;}if (event == Lifecycle.Event.ON_START || event == Lifecycle.Event.ON_RESUME) {for (int i = 0; i < mPendingData.size(); i++) {mObserver.onChanged(mPendingData.get(i));}mPendingData.clear();}}/*** onDestroy 时解除各方的观察和绑定,并清空数据*/@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)private void onDestroy() {mLiveData.removeObserver(this);mLiveData = null;mOwner.getLifecycle().removeObserver(this);mOwner = null;mPendingData.clear();mObserver = null;}public static <T> void bind(@NonNull LiveData<T> liveData,@NonNull LifecycleOwner owner,@NonNull Observer<? super T> observer) {if (owner.getLifecycle().getCurrentState() == Lifecycle.State.DESTROYED) {return;}new LiveEventObserver<>(liveData, owner, observer);}
}
复制代码

3.4、保证 LiveData 的事件更新

3.1 也说过要自己处理 postValue,其次要保证用我们自己定义的观察者,需要重写 public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer)

/*** 用作事件总线的 {@link MutableLiveData}** @see AsEventBus** @author funnywolf* @since 2019-05-18*/
public class EventMutableLiveData<T> extends MutableLiveData<T> {@Overridepublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {LiveEventObserver.bind(this, owner, observer);}@Overridepublic void postValue(T value) {LiveDataUtils.setValue(this, value);}
}/*** 用作事件总线的 {@link MediatorLiveData}** @see AsEventBus** @author funnywolf* @since 2019-05-18*/
public class EventMediatorLiveData<T> extends MediatorLiveData<T> {@Overridepublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {LiveEventObserver.bind(this, owner, observer);}@Overridepublic void postValue(T value) {LiveDataUtils.setValue(this, value);}
}/*** 该注解只用于 {@link androidx.lifecycle.LiveData},用于表示 LiveData 是当成事件总线用的,需要注意:* - 观察者在非激活状态(onStart 之后,onPause 之前)时不会产生回调,会丢失事件* - postValue 可能会被覆盖,只能用 setValue 来更新值* - LiveData 的事件都是黏性的,不使用时手动抛出一个 null 事件,以防下次绑定时会发送存在之前的旧数据;** @see LiveDataUtils* @see LiveEventObserver* @see EventMutableLiveData* @see EventMediatorLiveData** @author funnywolf* @since 2019-05-06*/
@Target(ElementType.FIELD)
public @interface AsEventBus {
}
复制代码

4、LiveDataUtils 中其他工具简介

这个工具包里还有其他一些我平时常用的小工具,这里简单分享下:

  • StateData 是含有状态和错误信息的数据包装类,因为 LiveData 只有一个 onChanged 回调,无法知道数据状态,所以搞了这个类
  • RxLiveData 继承自 MutableLiveData,实现了 DisposableObserver 接口,主要为了数据从 RxJavaLiveData 的转换
  • LiveDataBus,一个基于 LiveData 的事件总线,但是不推荐用。事件总线这玩意尽量不要用,除非是不用不行的场合,写的时候很香,之后维护起来很麻烦

5、总结

不得不说谷歌的生命周期库真的很强大,不仅给我们提供了现成的工具,还给予了我们方便 DIY 的能力,一个不到五百行的 LiveData 都能玩出很多花样。以上也只是自己的一些经验总结,难免会有不足,欢迎各位批评指正。

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

把 LiveData 用于事件传递那些坑相关推荐

  1. 【转】QT事件传递与事件过滤器

         [概览] 1.重载特定事件函数.    比如: mousePressEvent(),keyPressEvent(),  paintEvent() .      2.重新实现QObject:: ...

  2. Android自定义View,滑动,事件传递小结

    本文只总结知识点 欢迎补充,欢迎纠正.谢谢! #预备知识 Android控件框架 ####1. View树状图 Android的View树结构总是以一个ViewGroup开始,包含多个View或Vie ...

  3. Android之Android触摸事件传递机制

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/53431274 本文出自:[顾林海的博客] ##前言 关于Android ...

  4. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 七 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  5. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 六 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  6. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 五 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  7. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 三 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  8. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 二 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

  9. 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 一 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) [Andr ...

最新文章

  1. matplotlib生成指定大小的空白的白色图(故意保存)实战:当然也可以保存正常的有内容的图像
  2. java中的session对象,Java对象中Response与session对象的方法是什么?
  3. C语言内存管理之结构体
  4. service注入为null_如何解决quartz调度时候,job中的service为null的问题?
  5. ruby array_Ruby中带有示例的Array.keep_if方法
  6. NSURLSession下载
  7. Adobe Edge Animate 1.0 概述
  8. Python正则表达式:最短匹配
  9. 土地覆盖和土地利用的区别
  10. LINUX安装依赖库的解决办法
  11. 如何提高技术团队的工作效率
  12. win10用linux命令关机,Win10使用PowerShell命令让局域网电脑重启关机操作
  13. mPEG-Pyrene 甲氧基PEG芘丁酸
  14. 谈谈反向代理Nginx
  15. android SharePreference缓存存储List<Bean>
  16. mysql awr v1.0.2发布
  17. ARM Linux下的phys_to_virt/virt_to_phys函数
  18. spring用注解无法灵活注入带参构造函数解决办法
  19. 飞思卡尔芯片上锁常见原因及解锁方法
  20. 牛顿方法求平方根c语言,C语言之基本算法11—牛顿迭代法求平方根

热门文章

  1. 条件变量为什么要和互斥锁一起使用
  2. 华为鸿蒙os什么时候应用上线,华为 HarmonyOS :即日起,全新上线鸿蒙 100 问
  3. Linux下排查进程占用cpu高的问题
  4. 比赛结果预测_决策树_随机森林(通用 数据挖掘入门与实践-实验5)
  5. Struts2内置拦截器的简要介绍
  6. 利用java反射调用类的的私有方法
  7. IE8“开发人员工具”使用详解上(各级菜单详解)
  8. 介绍了Oracle数据库锁的种类及研究
  9. python作业6月14日
  10. 软件工程--第六周学习进度