/**
-
模拟请求,连续调用两次,第一次更新值为‘Java’,
-
第二次更新值为‘Kotlin’
*/
findViewById(R.id.button).setOnClickListener {
viewModel.updateTitlePost("Java")
viewModel.updateTitlePost("Kotlin")
}
点击模拟接口请求,连续调用两次,第一次更新值为‘Java’,第二次更新值为‘Kotlin’。
运行看看,
![image.png](https://img-blog.csdnimg.cn/img_convert/3021ddd04b80430c14cf4d78a93419c3.png)
可以看到控制台的log输出,不管点几次,只有Kotlin的值更新成功了,也就是符合小王同志的场景了,那第一次更新的Java值跑哪去了?
**为什么连续postValue两次,第一次的值会丢失?**
带着问题,我们来看看postValue内部的源码。
/**
-
Posts a task to a main thread to set the given value. So if you have a following code
-
executed in the main thread:
-
liveData.postValue(“a”);
-
liveData.setValue(“b”);
-
The value “b” would be set at first and later the main thread would override it with
-
the value “a”.
-
-
If you called this method multiple times before a main thread executed a posted task, only
-
the last value would be dispatched.
-
@param value The new value
*/
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
诶~~ 值得注意的是,官方已经在postValue的注释上说明了,
> If you called this method multiple times before a main thread executed a posted task, only the last value would be dispatched.
> 如果在主线程执行一个已发布的任务之前多次调用此方法,则只会分派最后一个值。
虽然官方直接解释了,我们还是要搞清楚原理不是。
接着看源码,postValue内部维护了一个boolean类型的postTask,利用synchronized对一个objec对象 mDataLock加了锁,并且内部有一个全局变量mPendingData,这是问题的关键。每次postValue会将新的值赋给mPendingData,然后会在一个Runnable中进行值的分发,且使用ArchTaskExecutor将该Runnable的任务发布到主线程中(题外话:这就是为什么postValue一直在主线程更新的原因)。
我通过断点的方式,跟踪了一下
viewModel.updateTitlePost(“Java”)
viewModel.updateTitlePost(“Kotlin”)
这两步的请求路线。 第一次调用viewModel.updateTitlePost(“Java”),postValue内部值如下图:
![image.png](https://img-blog.csdnimg.cn/img_convert/9777bc23b7a21cd619256c76497fb677.png)
传入的value值为“Java”,mPendingData等于初始值,postStask则为true,则直接走到了ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable)方法。 **记住这个时候mPendingData的值为“Java”**,
**我继续利用断点执行下一步,却发现直接执行了viewModel.updateTitlePost(“Kotlin”)开始了第二次的请求,并没有执行到mPostValueRunnable中,**
既然执行了viewModel.updateTitlePost(“Kotlin”)请求,postValue传入的参数也就是“Kotlin”,我们再来看看第二次请求的断点值:
![image.png](https://img-blog.csdnimg.cn/img_convert/a0c314f1041adc1e7f0a9b46993810fe.png)
value值为“Kotlin”,因为第一次postValue时,全局变量mPendingData值为“Java”,所以这次postTask = mPendingData == NOT\_SET所执行后的值为false,接着将最新的值“Kotlin”赋值给了mPendingData,**记住,此时的mPendingData值为Kotlin**。最后,如果postTask为false,则直接return了,也就是说第二次的postValue压根不会再去 postToMainThread(mPostValueRunnable)。
当第二次请求postValue执行后,mPostValueRunnable才开始执行,
![image.png](https://img-blog.csdnimg.cn/img_convert/2898fa6f02a407679a20f16ac98705ea.png)
这个时候全局变量mPendingData的值已经变为“Kotlin”了,直接携带“Kotlin”这个参数在Runnable中 执行setValue,也就是最后触发了liveData的onChange回调,也就是文章一开始提出的,只会收到一次onChange。
至此,postValue为什么会丢失值的问题已经分析结束了,那我们再来**捋一捋**。
* **postValue方法内部其实是将值得回调逻辑放到了Runnable中,再post给Handler,利用Handler在主线程中更新,因此从postValue到执行Runnable,中间是存在时间差的。**
* **在执行Runnable之前,因为是连续调用的postValue,第一次mPendingData的值被第二次调用时的值覆盖掉了,最后执行Runnable时,mPendingData的值只能是最新的那个,也就是带着第二次的值触发onChange回调给UI。**
“懂了,懂了,付老板yyds”,小王同志听了我的一番分(吹)析(皮)点了点头。
“那还有一个问题,**既然postValue最后会执行到Runnable,那连续两次postValue,为什么没有两次Runnable的执行呢?**”
“其实这个问题在上面分析中已经给出了答案,在postValue函数内部维护了一个boolean类型的postTask,而postTask是true还是false是由mPendingData值来决定的,第一次执行postValue,mPendingData == NOT\_SET初始值,postTask则为true,接着将参数值赋值给了mPendingData,然后开始post了一个Runnable。
这个时候开始了第二次的postValue,mPendingData由于不再等于初始值NOT\_SET,则postTask为false,postTask为false,那就直接return了,就不会再执行一个Runnable了,这就是为什么没有两次Runnable的执行。
而在Runnable中,mPendingData会被重置为NOT\_SET,开始接受新一轮的执行逻辑。”
小王默默的记在了心里。
“**那既然postValue丢失旧值是因为需要post Runnable的缘故,那setValue没有这一步,是不是就不会丢失值了。**”
“哟~~ 不错嘛,学会举一反三了,没错的,setValue不会丢值,这也是postValue和setValue的不同之处。我们可以实践看看。
其他不变,将postValue改为setValue,
![image.png](https://img-blog.csdnimg.cn/img_convert/b29b782f8166c056571f964cbe131edf.png)
从输出上可以看到activity中“Java”和“Kotlin”的值都收到了。”
这也就验证了setValue和postValue不一样,并不会丢失值。
于是,小王同志解决了bug开开心心的走了,我也戴上耳机,继续我的代码之旅。
**推荐阅读:**
[Flutter实战项目开源]( )
[「Kotlin篇」原来,协程是这么挂起的]( )
[【Jetpack篇】LiveData取代EventBus?LiveData的通信原理和粘性事件刨析]( )
1713886180999204")
[【Jetpack篇】LiveData取代EventBus?LiveData的通信原理和粘性事件刨析]( )
[]( )最后
------------------------------------------------------------------
分享给大家一份面试题合集。
下面的题目都是在Android交流群大家在面试时遇到的,如果大家有好的题目或者好的见解欢迎分享,楼主将长期维护此帖。
**参考解析**:郭霖、鸿洋、玉刚、极客时间、腾讯课堂…
**内容特点**:条理清晰,含图像化表示更加易懂。
**内容概要**:包括 Handler、Activity相关、Fragment、service、布局优化、AsyncTask相关
、Android 事件分发机制、 Binder、Android 高级必备 :AMS,WMS,PMS、Glide、 Android 组件化与插件化等面试题和技术栈!
![image](https://img-blog.csdnimg.cn/img_convert/c47afcb94a27d079dd57145faf431581.webp)
[]( )Handler 相关知识,面试必问!
----------------------------------------------------------------------------------
常问的点:
Handler Looper Message 关系是什么?
Messagequeue 的数据结构是什么?为什么要用这个数据结构?
如何在子线程中创建 Handler?
Handler post 方法原理?
Android消息机制的原理及源码解析
Android Handler 消息机制
![image](https://img-blog.csdnimg.cn/img_convert/e3256ffd4125b1ccd91e6f0c8e32ba06.webp)
[]( )Activity 相关
---------------------------------------------------------------------------
启动模式以及使用场景?
onNewIntent()和onConfigurationChanged()
onSaveInstanceState()和onRestoreInstanceState()
Activity 到底是如何启动的
启动模式以及使用场景
onSaveInstanceState以及onRestoreInstanceState使用
onConfigurationChanged使用以及问题解决
Activity 启动流程解析
![image](https://img-blog.csdnimg.cn/img_convert/c47ed523fe70b4595585dbde0c2d0ed2.webp)
[]( )Fragment
------------------------------------------------------------------------
Fragment 生命周期和 Activity 对比
Fragment 之间如何进行通信
Fragment的startActivityForResult
Fragment重叠问题
Fragment 初探
Fragment 重叠, 如何通信
Fragment生命周期
![image](https://img-blog.csdnimg.cn/img_convert/26bc720be75e71258829b29ca5f5c372.webp)
[]( )Service 相关
--------------------------------------------------------------------------
进程保活
Service的运行线程(生命周期方法全部在主线程)
Service启动方式以及如何停止
ServiceConnection里面的回调方法运行在哪个线程?
startService 和 bingService区别
进程保活一般套路
关于进程保活你需要知道的一切
![image](https://img-blog.csdnimg.cn/img_convert/e569c4e55cd0c4bbfa16bb322019b66a.webp)
[]( )Android布局优化之ViewStub、include、merge
--------------------------------------------------------------------------------------------------
什么情况下使用 ViewStub、include、merge?
他们的原理是什么?
ViewStub、include、merge概念解析
Android布局优化之ViewStub、include、merge使用与源码分析
![image](https://img-blog.csdnimg.cn/img_convert/c70a8e6f5f408544e22e4933299909d0.webp)
[]( )BroadcastReceiver 相关
------------------------------------------------------------------------------------
注册方式,优先级
广播类型,区别
广播的使用场景,原理
Android广播动态静态注册
常见使用以及流程解析
广播源码解析
![image](https://img-blog.csdnimg.cn/img_convert/c1e690a6d9b2bbfe2ef410500222f9aa.webp)
[]( )AsyncTask相关
---------------------------------------------------------------------------
AsyncTask是串行还是并行执行?
AsyncTask随着安卓版本的变迁
AsyncTask完全解析
串行还是并行
![image](https://img-blog.csdnimg.cn/img_convert/04c78b7764e4c731a259f421ee379012.webp)
[]( )Android 事件分发机制
------------------------------------------------------------------------------
onTouch和onTouchEvent区别,调用顺序
dispatchTouchEvent, onTouchEvent, onInterceptTouchEvent 方法顺序以及使用场景
滑动冲突,如何解决
事件分发机制
事件分发解析
dispatchTouchEvent, onTouchEvent, onInterceptTouchEvent方法的使用场景解析
![image](https://img-blog.csdnimg.cn/img_convert/cb5310dc906182a46bc40420040b94a7.webp)
[]( )Android View 绘制流程
---------------------------------------------------------------------------------
简述 View 绘制流程
onMeasure, onlayout, ondraw方法中需要注意的点
如何进行自定义 View
view 重绘机制
* Android LayoutInflater原理分析,带你一步步深入了解View(一)
* Android视图状态及重绘流程分析,带你一步步深入了解View(二)
* Android视图状态及重绘流程分析,带你一步步深入了解View(三)
* Android自定义View的实现方法,带你一步步深入了解View(四)
![image](https://img-blog.csdnimg.cn/img_convert/50a1cd758f00c0706fd38b050dbc25f3.webp)
[]( )Android Window、Activity、DecorView以及ViewRoot
-----------------------------------------------------------------------------------------------------------
Window、Activity、DecorView以及ViewRoot之间的关系
![image](https://img-blog.csdnimg.cn/img_convert/c074ca8814340217aadb02bd6caf5f18.webp)
[]( )Android 的核心 Binder 多进程 AIDL
-------------------------------------------------------------------------------------------
常见的 IPC 机制以及使用场景
为什么安卓要用 binder 进行跨进程传输
多进程带来的问题
* AIDL 使用浅析
* binder 原理解析
* binder 最底层解析
* 多进程通信方式以及带来的问题
* 多进程通信方式对比
![image](https://img-blog.csdnimg.cn/img_convert/5b5d3b8cf7914e93bf187e0d05006d38.webp)
[]( )Android 高级必备 :AMS,WMS,PMS
-----------------------------------------------------------------------------------------
AMS,WMS,PMS 创建过程
* AMS,WMS,PMS全解析
* AMS启动流程
* WindowManagerService启动过程解析
* PMS 启动流程解析
![image](https://img-blog.csdnimg.cn/img_convert/06a33a713f1a110ea8477ab97ab9148b.webp)
[]( )Android ANR
---------------------------------------------------------------------------
# 最后
我一直以来都有整理练习大厂面试题的习惯,有随时跳出舒服圈的准备,也许求职者已经很满意现在的工作,薪酬,觉得习惯而且安逸。
不过如果公司突然倒闭,或者部门被裁减,还能找到这样或者更好的工作吗?
我建议各位,多刷刷面试题,知道最新的技术,每三个月可以去面试一两家公司,因为你已经有不错的工作了,所以可以带着轻松的心态去面试,同时也可以增加面试的经验。
我可以将最近整理的一线互联网公司面试真题+解析分享给大家,大概花了三个月的时间整理2246页,帮助大家学习进步。
> **由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是部分内容截图:**
![](https://img-blog.csdnimg.cn/img_convert/adb37d2f4d2185645ce1cb4945832a5a.webp?x-oss-process=image/format,png)
![部分目录截图](https://img-blog.csdnimg.cn/img_convert/3a7dadab6e966ab0d7abed2a5409d18f.webp?x-oss-process=image/format,png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
大厂面试题的习惯,有随时跳出舒服圈的准备,也许求职者已经很满意现在的工作,薪酬,觉得习惯而且安逸。
不过如果公司突然倒闭,或者部门被裁减,还能找到这样或者更好的工作吗?
我建议各位,多刷刷面试题,知道最新的技术,每三个月可以去面试一两家公司,因为你已经有不错的工作了,所以可以带着轻松的心态去面试,同时也可以增加面试的经验。
我可以将最近整理的一线互联网公司面试真题+解析分享给大家,大概花了三个月的时间整理2246页,帮助大家学习进步。
> **由于篇幅限制,文档的详解资料太全面,细节内容太多,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!以下是部分内容截图:**
[外链图片转存中...(img-JUnXmFxf-1714527432827)]
[外链图片转存中...(img-pY6lRHw5-1714527432827)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化学习资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618156601)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**