发布于2021-06-12 13:48 阅读(284) 评论(0) 点赞(28) 收藏(5)
很多面试官喜欢会就一个问题不断深入追问。
例如一个小小的 LiveData 的 postValue,就可能会问出一连串问题:
postValue
与 setValue
一样都是用来更新 LiveData 数据的方法:
- liveData.postValue("a");
- liveData.setValue("b");
上面代码,a 在 b 之后才被更新。
postValue 使用不当,可能发生接收到数据变更的通知:
If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.
如上,源码的注释中明确记载了,当连续调用 postValue 时,有可能只会收到最后一次数据更新通知。
梳理源码可以了解其中原由:
- protected void postValue(T value) {
- boolean postTask;
- synchronized (mDataLock) {
- postTask = mPendingData == NOT_SET;
- mPendingData = value;
- }
- if (!postTask) {
- return;
- }
- ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
- }
mPendingData
被成功赋值 value 后,post 了一个 Runnable
mPostValueRunnable
的实现如下:
- private final Runnable mPostValueRunnable = new Runnable() {
- @SuppressWarnings("unchecked")
- @Override
- public void run() {
- Object newValue;
- synchronized (mDataLock) {
- newValue = mPendingData;
- mPendingData = NOT_SET;
- }
- setValue((T) newValue);
- }
- };
postValue 将数据存入 mPendingData
,mPostValueRunnable
在UI线程消费mPendingData
。
在 Runnable 中 mPendingData
值还没有被消费之前,即使连续 postValue , 也不会 post 新的 Runnable
mPendingData
的生产 (赋值) 和消费(赋 NOT_SET) 需要加锁
这也就是当连续 postValue 时只会收到最后一次通知的原因。
源码梳理过了,但是为什么要这样设计呢?
当 mPenddingData
中有数据不断更新时,为什么 Runnable 不是每次都 post,而是等待到最后只 post 一次?
一种理解是为了兼顾性能,UI只需显示最终状态即可,省略中间态造成的频发刷新。这或许是设计目的之一,但是一个更为合理的解释是:即使 post 多次也没有意义,所以只 post 一次即可
我们知道,对于 setValue 来说,连续调用多次,数据会依次更新:
如下,订阅方一次收到 a b 的通知
- liveData.setValue("a");
- liveData.setValue("b");
通过源码可知,dispatchingValue()
中同步调用 Observer#onChanged()
,依次通知订阅方:
- //setValue源码
-
- @MainThread
- protected void setValue(T value) {
- assertMainThread("setValue");
- mVersion++;
- mData = value;
- dispatchingValue(null);
- }
但对于 postValue,如果当 value 变化时,我们立即post,而不进行阻塞
- protected void postValue(T value) {
- mPendingData = value;
- ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
- }
-
- private final Runnable mPostValueRunnable = new Runnable() {
- public void run() {
- setValue((T) mPendingData);
- }
- };
- liveData.postValue("a")
- liveData.postValue("b")
由于线程切换的开销,连续调用 postValue,收到通知只能是b、b,无法收到a。
因此,post 多次已无意义,一次即可。
前面已经知道,是否 post 取决于对 mPendingData 的判断(是否为 NOT_SET)。因为要在多线程环境中访问 mPendingData ,不加读写锁无法保证其线程安全。
- protected void postValue(T value) {
- boolean postTask = mPendingData == NOT_SET; // --1
- mPendingData = value; // --2
- if (!postTask) {
- return;
- }
- ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
- }
-
- private final Runnable mPostValueRunnable = new Runnable() {
- public void run() {
- Object newValue = mPendingData;
- mPendingData = NOT_SET; // --3
- setValue((T) newValue);
- }
- };
如上,如果在 1 和 2 之间,执行了 3,则 2 中设置的值将无法得到更新
如何避免在多线程环境下不漏掉任何一个通知? 比较好的思路是借助 RxJava 这样的流式框架,任何数据更新都以数据流的形式发射出来,这样就不会丢失了。
- fun <T> Observable<T>.toLiveData(): LiveData<T> = RxLiveData(this)
-
- class RxLiveData<T>(
- private val observable: Observable<T>
- ) : LiveData<T>() {
- private var disposable: Disposable? = null
-
- override fun onActive() {
- disposable = observable
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe({
- setValue(it)
- }, {
- setValue(null)
- })
- }
-
- override fun onInactive() {
- disposable?.dispose()
- }
- }
想要保证事件在线程切换过程中的顺序性和完整性,需要使用RxJava这样的流式框架。
有时候面试官会使用追问的形式来挖掘候选人的技术深度,所以大家在准备面试时要多问自己几个问什么,知其然并知其所以然。
最后,在这里我给大家提供一份自己收录整理上述Android技术体系图相关的高级进阶必刷文档(已整理成PDF模板),包含了Android 基础,进阶,架构、 Kotlin,Flutter,NDK,小程序,面试题,面经,都有收录,供君一览。
还有 高级架构技术进阶思维图帮助大家学习提升进阶,也节省大家在网上搜索的时间来学习,也希望大家可以和身边好友一起学习。最后祝愿大家都能有个光明的未来。
有需要的朋友可以" 查阅下方的Github项目 "即可;
原文链接:https://blog.csdn.net/qq_39477770/article/details/117753728
作者:飞翔公园
链接:http://www.javaheidong.com/blog/article/222040/79062a22f8ba0f16beaa/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!