Jetpack LiveData 这个组件对于现在的 Android 开发者来说应该是很熟悉的了吧?那么,你是否也曾经困扰于以下的业务场景呢?(哈哈哈哈哈,你必须困扰,不然本篇文章我也吹不下去了。当然,即使你没有遇到过这种业务场景,本篇文章也为你以后遇到相同问题时(大概率会遇到)提供了解决方案和解决思路)
这里先放下 GitHub 链接:https://github.com/leavesC/EventLiveData
这里先来假设两种场景,先让读者体会到 LiveData 相对实际业务上具有的局限性
假设当前你的 App 包含一个圈子列表页面,每个圈子 item 包含了一个按钮用于改变对此圈子的关注状态,点击 item 可以跳转到圈子详情页,在详情页也包含一个按钮用于改变圈子的关注状态
然后,产品经理要求:当从圈子列表页跳转到圈子详情页时,在圈子详情页改变了关注状态后,回到圈子列表页时也要实时显示当前的最新关注状态
此时,凭直觉我们就会写这样一行代码:
companion object { //String 表示圈子ID,Boolean 表示对该圈子的关注状态 val focusLiveData = MutableLiveData<Pair<String, Boolean>>() }
声明一个全局静态变量 focusLiveData
,在圈子详情页如果改变了关注状态,则向 focusLiveData postValue
,透传出当前最新的关注状态。然后在圈子列表页面对 focusLiveData
进行监听,当接收到事件回调时则自动刷新列表从而拿到最新的关注状态
依靠直觉写出以上代码后,产品经理的需求我们也完美地实现了?并没有!因为此时又带来了一个新的问题!正常情况下当我们进入圈子列表页面时是会自动通过网络请求从服务器拉取数据的,而如果此时 focusLiveData
本身就是有值的,那我们刚进入圈子列表页面时就会收到旧值导致的事件回调!!!从而导致页面被刷新了两次,此时测试可能就会来给你提 bug 了。而这问题的出现根源就是因为 focusLiveData 会持有旧值,相当于使得 Activity 收到了一个粘性事件
如果 UI 设计师对圈子列表页的关注按钮设计了一个非常花里胡哨的动画,在关注时要执行一个几百毫秒的动画,那么按照以上的代码逻辑,当用户在圈子详情页关注了圈子后,回到圈子列表页时用户就会非常奇怪地看到关注按钮自己执行了动画。然后此时测试又来提 bug 了,要求在圈子详情页改变了关注状态后,在圈子列表页不要执行动画,直接改变关注状态即可。此时我们就不得不对动画的执行条件做区分了,从而加大我们业务的复杂度。而这问题的出现根源就是因为 LiveData 在绑定了生命周期的情况下只会在 Activity onResume 的时候才进行回调,而此时用户都已经可以看到 Activity 处于前台了
很好,基于以上现状,我们再来总结下我们的需求:
基于以上需求,我实现了 EventLiveData 来扩展原生 LiveData 的功能,在继续保持原生 LiveData 原有功能的同时,也扩展了以下几个功能入口:
首先,你可以把 EventLiveData 当做原生的 LiveData 来使用,以下方法和 LiveData 完全一样:
/** * 在生命周期安全的整体保障上和 LiveData 完全一样 * 在 onResume 时接收 Observer 回调,并在 onDestroy 前自动移除监听 * @param owner * @param observer */ @MainThread fun observe(owner: LifecycleOwner, observer: Observer<T>) { ··· } /** * 不具备生命周期安全的保障,使用上和 LiveData 完全一样 * @param observer */ @MainThread fun observeForever(observer: Observer<T>) { ··· }
如果你希望在 observe 后不会收到旧值,则可以使用 observeEvent
、observeEventForever
两个方法,两者的区别仅在于是否绑定了生命周期:
/** * 在生命周期安全的整体保障上和 LiveData 完全一样 * 在 onResume 时接收 Observer 回调,并在 onDestroy 前自动移除监听 * 但此方法不会向 Observer 回调旧值,即 EventLiveData 只会向 Observer 回调在调用 observeEvent 之后收到的值 * @param owner * @param observer */ @MainThread fun observeEvent(owner: LifecycleOwner, observer: Observer<T>) { ··· } /** * 不具备生命周期安全的保障 * 此方法不会向 Observer 回调旧值,即 EventLiveData 只会向 Observer 回调在调用 observeEvent 之后收到的值 * @param observer */ @MainThread fun observeEventForever(observer: Observer<T>) { ··· }
如果你希望在 onCreate 之后和 onDestory 之前均能收到数据回调,则可以使用 observeAlive
方法
/** * 相比 LiveData 会具备更长的生命周期 * 在 onCreate 之后和 onDestroy 之前均能收到 Observer 回调,并在 onDestroy 时自动移除监听 * @param owner * @param observer */ @MainThread fun observeAlive(owner: LifecycleOwner, observer: Observer<T>) { ··· }
如果你即不想收到旧值,又希望在 onCreate 之后和 onDestory 之前均能收到数据回调,则可以使用 observeAliveEvent
/** * 相比 LiveData 会具备更长的生命周期 * 在 onCreate 之后和 onDestroy 之前均能收到 Observer 回调,并在 onDestroy 时自动移除监听 * 但此方法不会向 Observer 回调旧值,即 EventLiveData 只会向 Observer 回调在调用 observeEvent 之后收到的值 * @param owner * @param observer */ @MainThread fun observeAliveEvent(owner: LifecycleOwner, observer: Observer<T>) { ··· }
向 EventLiveData 传递值的入口统一为了 postValue(value: T)
方法,内部会根据调用者所在线程自动进行线程切换
fun postValue(value: T) { ··· }
EventLiveData 是基于原生 LiveData 来实现的,在理解了 LiveData 的实现原理后,其实我们就可以比较简单地来改造其源码实现自定义要求了。不了解 LiveData 实现原理的同学可以先看下我写的另外一篇文章:从源码看 Jetpack(3)-LiveData源码解析
这里再来简单介绍下 EventLiveData 的实现原理
LiveData 内部包含一个 mVersion
来记录当前值的新旧程度,当外部传递了新值时(不管是 setValue 还是 postValue),mVersion 均会递增+1
@MainThread private fun setValue(value: T) { assertMainThread( "setValue" ) mVersion++ mData = value dispatchingValue(null) }
同时 ObserverWrapper 内部包含一个 mLastVersion
用于标记 Observer 内最后一个被回调的 value 的新旧程度
private abstract class ObserverWrapper { //外部传进来的对 LiveData 进行数据监听的 Observer final Observer<? super T> mObserver; //用于标记 mObserver 是否处于活跃状态 boolean mActive; //用于标记 Observer 内最后一个被回调的 value 的新旧程度 int mLastVersion = START_VERSION; ObserverWrapper(Observer<? super T> observer) { mObserver = observer; } }
而 considerNotify
方法就是根据 mLastVersion 的大小来决定是否需要向外部传进来 Observer 回调值,那么我们只要控制 Observer 的 mLastVersion 的初始值大小不就可以避免旧值的通知了吗?
private void considerNotify(ObserverWrapper observer) { ··· if (observer.mLastVersion >= mVersion) { return; } observer.mLastVersion = mVersion; observer.mObserver.onChanged((T) mData); }
再然后,LifecycleBoundObserver 的 shouldBeActive()
方法就限制了只有当 Lifecycle 的当前状态是 STARTED 或者 RESUMED 时才进行数据回调,那么我们只要改变此限制条件,就可以增大 Observer 的有效生命周期范围了
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver { @NonNull final LifecycleOwner mOwner; LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) { super(observer); mOwner = owner; } @Override boolean shouldBeActive() { //只有当 Lifecycle 的当前状态是 STARTED 或者 RESUMED 时 //才认为 Lifecycle 是处于活跃状态 return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED); } }
EventLiveData 的实现原理就是基于以上两点,虽然听起来简单,但这都是基于了解了 LiveData 的实现逻辑的基础上,不熟悉的同学可以先看下我对 LiveData 的源码解析文章
EventLiveData 已开源到 GitHub:https://github.com/leavesC/EventLiveData ,且已发布到第三方代码托管库,读者可以直接以以下方法来引入依赖
添加远程依赖库地址
allprojects { repositories { maven { url 'https://jitpack.io' } } }
引入依赖:
dependencies { implementation 'com.github.leavesC:EventLiveData:0.1.0' }
感谢阅读