众所周知,程序员面试的时候,很多面试官喜欢会就一个问题不断深入追问。

例如一个小小的 LiveData 的 postValue,就可能会问出一连串问题:

postValue 与 setValue

postValuesetValue 一样都是用来更新 LiveData 数据的方法:

  • setValue 只能在主线程调用,同步更新数据
  • postValue 可在后台线程调用,其内部会切换到主线程调用 setValue
liveData.postValue("a");
liveData.setValue("b");

上面代码,a 在 b 之后才被更新。

postValue 收不到通知

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")@Overridepublic void run() {Object newValue;synchronized (mDataLock) {newValue = mPendingData;mPendingData = NOT_SET;}setValue((T) newValue);}
};
  • postValue 将数据存入 mPendingDatamPostValueRunnable 在UI线程消费mPendingData

  • 在 Runnable 中 mPendingData 值还没有被消费之前,即使连续 postValue , 也不会 post 新的 Runnable

  • mPendingData 的生产 (赋值) 和消费(赋 NOT_SET) 需要加锁

这也就是当连续 postValue 时只会收到最后一次通知的原因。

源码梳理过了,但是为什么要这样设计呢?

为什么 Runnable 只 post 一次?

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; // --1mPendingData = value; // --2if (!postTask) {return;}ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}private final Runnable mPostValueRunnable = new Runnable() {public void run() {Object newValue = mPendingData;mPendingData = NOT_SET; // --3setValue((T) newValue);}
};

如上,如果在 1 和 2 之间,执行了 3,则 2 中设置的值将无法得到更新

使用RxJava替换LiveData

如何避免在多线程环境下不漏掉任何一个通知? 比较好的思路是借助 RxJava 这样的流式框架,任何数据更新都以数据流的形式发射出来,这样就不会丢失了。

fun <T> Observable<T>.toLiveData(): LiveData<T> = RxLiveData(this)class RxLiveData<T>(private val observable: Observable<T>
) : LiveData<T>() {private var disposable: Disposable? = nulloverride fun onActive() {disposable = observable.observeOn(AndroidSchedulers.mainThread()).subscribe({setValue(it)}, {setValue(null)})}override fun onInactive() {disposable?.dispose()}
}

最后

想要保证事件在线程切换过程中的顺序性和完整性,需要使用RxJava这样的流式框架。

有时候面试官会使用追问的形式来挖掘候选人的技术深度,所以大家在准备面试时要多问自己几个问什么,知其然并知其所以然。

当然,我也不赞同这种刨根问底式的拷问方式,尤其是揪着一些没有实用价值的细枝末节不放。所以本文也是提醒广大面试官,挖掘深度的同时要注意分寸,不能以将候选人难倒为目标来问问题。

好了,今天的文章就到这里,感谢阅读,喜欢的话不要忘了三连。大家的支持和认可,是我分享的最大动力。

Android高级开发系统进阶笔记、最新面试复习笔记PDF,我的GitHub

原文首发:https://juejin.cn/post/6971608728042733605

Android面试:说一下 LiveData 的 postValue ?与SetValue有什么区别?连续调用会有什么问题?为什么?相关推荐

  1. Android面试必问框架原理

    Android面试必问框架原理 volatile的实现原理 synchronized的实现原理 join方法实现原理 CAS无锁编程的原理 ReentrantLock的实现原理 AQS的大致实现思路 ...

  2. Android Jetpack组件之 LiveData使用-源码

    1.前言 最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面. A ...

  3. Android Architecture Components Part2:LiveData

    感谢你的再次光临,欢迎来到Android Architecture Components(ACC)系列文章.上篇文章我们一起讨论了Room,通过Room我们能够方便的操作App的数据库.如果你的App ...

  4. “金三银四” “阿里” 我去定了,谁也拦不住我,这份《Android面试宝典》说的

    前言: 面试,跳槽,每天都在发生,而对程序员来说"金三银四"更是面试和跳槽的高峰期,跳槽,更是很常见的,对于每个人来说,跳槽的意义也各不相同,可能是一个人更向往一个更大的平台,更好 ...

  5. 金三银四阿里我去定了,谁也拦不住我,这份《Android面试宝典》说的

    前言: 面试,跳槽,每天都在发生,而对程序员来说"金三银四"更是面试和跳槽的高峰期,跳槽,更是很常见的,对于每个人来说,跳槽的意义也各不相同,可能是一个人更向往一个更大的平台,更好 ...

  6. 网易被裁后,68天吃透这份阿里学长甩我的Android面试笔记,竟让我收到字节跳动和小米offer

    自我情况介绍一下: 楼主双非本科,17年毕业,学历背景一般,之前一直在网易工作,生活状态还算是稳定,国庆节后突然被裁彻底打破了我的生活节奏,将近一个月都处在懵逼状态(哪个环节出问题了,导致被裁),在咨 ...

  7. 今年Android面试必问的这些技术面,面试心得体会

    前言 不清楚你是不是知道,咱们中国有相当大的一部分软件公司,他们的软件开发团队都小的可怜,甚至只有1-3个人,连一个项目小组都算不上,而这样的团队却要承担一个软件公司所有的软件开发任务,在软件上线和开 ...

  8. 安卓已死?这些年Android面试的那些套路,实战解析

    历时半年,我们终于整理出了这份市面上最全面的最新Android面试题解析大全! 章节目录 第一章:Android 基础 面试题 第二章:Android 高级 面试题 第三章:开源框架实战面试解析 第四 ...

  9. Android Jetpack Components of LiveData 学习笔记

    Android Jetpack Components of Lifecycle 学习笔记 Android Jetpack Components of LiveData 学习笔记 Android Jet ...

  10. Android面试问题汇总

    GitHub持续更新:(声明:本答案为个人收集与总结并非标准答案,仅供参考,如有错误还望指出,谢谢!如有重复可能是常问问题) ArrayList的使用,ArrayList使用过程中有没有遇到过坑. 参 ...

最新文章

  1. curl_exec() 执行的时候发送不出去_为什么端口明明开着,nmap却扫描不出来,看老司机怎么指点迷津...
  2. html前端页面的基本骨架是,web前端入门到实战:css实现的骨架屏方案
  3. asp.net的处理机制(.ashx/.aspx)
  4. talentcentral测评结果_WinTalent人才测评系统
  5. java spring文件下载_SpringMVC实现文件上传和下载的工具类
  6. Andirod——网络连接(HttpURLConnection)
  7. c51语言的数据存储模式,第5章项目三单片机存储结构及C51语言.ppt
  8. multiple build commands for output file
  9. 计算机三四级网络技术,全国计算机等级考试四级网络技术论述题真题(2-3)
  10. Android 上专为视屏直播打造的轻量级弹幕库(100 多 kb)
  11. 2021-01-29 大数据课程笔记 day9
  12. js跨域问题 ajax跨域问题?
  13. Verilog Language--Modules:Hierachy--Module add
  14. 在windows10上写自己的第一个python代码
  15. Life:歌曲学习之教一个不会唱歌的人学会唱出《情非得已》、《海阔天空》、《红日》、《老男孩》等歌曲
  16. 委托代理问题------The principal-agent problem
  17. gpx读写java_GPX Parser for Java?
  18. 概率论与数理统计张宇9讲 第七讲 大数定律与中心极限定理
  19. 新加坡国立大学Xinchao Wang老师招收图神经网络方向博士/博后
  20. 关于如何租一个云服务器进行使用

热门文章

  1. Android icon图标网站
  2. 爬虫实现对于百度文库内容的爬取
  3. 侙程序错误怎么找c语言,log4j 施用 - 汉字转换成拼音的种(转) - 遏止EditText弹出输入法_169IT.COM...
  4. 一键识别图片中的表格数据,并转为Excel
  5. iOS-AppStore上线被拒的各种理由...
  6. 服务器代维护 云桥,Citrix
  7. java收费_一文看懂Java收费 附可替换方案!
  8. 一文搞定学术英语写作 (斯坦福SCI论文写作课程笔记)
  9. CSS3实现两头细中间粗的线(纵向)与一线两色\渐变线\文字投影\文字渐变等
  10. vue打包报错error in ./node_modules/view-design/dist/styles/fonts/ionicons.svg?v=3.0.0