当前位置: 首页 > news >正文

LiveData observe 注册和接收信息

class LifecycleDemo1ViewModel : ViewModel() { //写入 private var _userName = MutableLiveData<String>() //对外只读 val userName: LiveData<String> = _userName fun testPostValue(){ viewModelScope.launch() { _userName.postValue("test kotlin") //注意和setValue的区别} } }
第一种通用写法: demo1ViewModel.userName.observe(this){ name -> Log.d("AAA", "1 userName = " + name) } 第二种回调onChanged比较容易理解 demo1ViewModel.userName.observe(this, object : Observer<String>{ override funonChanged(value: String) { Log.d("AAA", "2 userName = " + value) } })子线程使用:postValue 主线程使用:setValue 最终子线程postValue也会转换成主线程调用setValue的 使用观察者模式,observe注册:owner.getLifecycle().addObserver(wrapper);observer.mObserver.onChanged((T) mData); // 最终回调你的 Lambda

我们从源码级别postValueonChanged的每一步彻底拆解。这个过程可以精确划分为4 个阶段

为了让你看透本质,我会带上源码中的关键变量名(基于 AndroidX Lifecycle 2.x 版本)。


阶段 1:postValue—— 线程调度与暂存

当你调用_userName.postValue("test kotlin")时,源码内部做了两件事:

java

// LiveData.java 源码 private volatile Object mPendingData = NOT_SET; // 暂存变量 private final Runnable mPostValueRunnable = new Runnable() { @Override public void run() { Object newValue = mPendingData; mPendingData = NOT_SET; // 取出后立即清空 setValue((T) newValue); } }; protected void postValue(T value) { boolean postTask; synchronized (mDataLock) { postTask = mPendingData == NOT_SET; // 判断是否已有未处理的任务 mPendingData = value; // ① 将新值暂存到 mPendingData } if (!postTask) { return; // 如果已经有任务排队,直接返回,只更新暂存值 } // ② 通过主线程 Handler 发送 Runnable ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable); }

关键细节

  • mPendingData是“暂存位”:不管主线程是否空闲,postValue总是立即把新值赋值给mPendingData

  • 防高频堆积:如果当前主线程消息队列里已经有一个mPostValueRunnable在排队了,再次调用postValue时,postTaskfalse不会再发送新的 Runnable。这意味着:高频调用postValue时,只会触发一次主线程任务,但暂存的值会被覆盖为最后一次设置的值。


阶段 2:主线程执行Runnable—— 调setValue

当主线程空闲,Looper 取出这个消息,执行mPostValueRunnablerun()

java

// 接上面的 run() public void run() { Object newValue = mPendingData; mPendingData = NOT_SET; // ① 取走并清空暂存 setValue((T) newValue); // ② 调用 setValue }

进入setValue(T value)

java

@MainThread protected void setValue(T value) { assertMainThread("setValue"); // 确保在主线程 synchronized (mDataLock) { mData = value; // ① 正式更新内部数据 mVersion++; // ② 版本号自增 +1 } dispatchingValue(null); // ③ 触发分发 }

关键细节

  • 此时数据才算真正存入mData(LiveData 持有的最终值)。

  • mVersion自增是通知的核心依据。初始版本通常是-1,每次setValue都会+1


阶段 3:dispatchingValue—— 遍历与并发保护

进入dispatchingValue(null)(传null代表通知所有观察者):

java

void dispatchingValue(@Nullable ObserverWrapper initiator) { if (mDispatchingValue) { // 如果正在分发中 mDispatchInvalidated = true; // 打标记,不重复遍历 return; } mDispatchingValue = true; do { mDispatchInvalidated = false; // 遍历所有观察者(存储在 SafeIterableMap 中) for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator = mObservers.iteratorWithAdditions(); iterator.hasNext(); ) { if (mDispatchInvalidated) break; // 如果被标记失效,重来 considerNotify(iterator.next().getValue()); // 逐一通知 } } while (mDispatchInvalidated); mDispatchingValue = false; }

关键细节

  • 防并发修改:如果在遍历过程中,有新的观察者注册或数据再次变更,会通过mDispatchInvalidated标记,让外层do-while重新遍历,保证所有观察者都能收到最新状态,不会崩溃。


阶段 4:considerNotify—— 生命周期检查 + 粘性触发(最终回调)

这是决定你能否收到回调的“最后一关”:

java

private void considerNotify(ObserverWrapper observer) { if (!observer.mActive) return; // 如果宿主(Activity)已销毁,直接跳过 // 核心:检查宿主的当前生命周期是否 >= STARTED if (!observer.shouldBeActive()) { observer.activeStateChanged(false); return; } // 关键:粘性逻辑判断 if (observer.mLastVersion >= mVersion) { return; // 如果观察者记录的版本号 >= 当前版本号,说明已分发过,跳过 } observer.mLastVersion = mVersion; // 更新观察者的版本号observer.mObserver.onChanged((T) mData); // 最终回调你的 Lambda}

两个关键过滤条件

  1. 生命周期屏障(可见性)
    shouldBeActive()检查的是LifecycleOwner的状态是否为STARTEDRESUMED
    如果你的 Activity 在后台(PAUSED),此处直接返回,不会执行回调,避免更新 UI 崩溃。

  2. 版本号屏障(粘性/防重复)
    即使生命周期满足,如果observer.mLastVersion == mVersion,也不回调。
    但你的场景中,postValue使mVersion0变成了1,而新注册的observer.mLastVersion初始是-1,因为-1 < 1,所以条件通过,立刻回调—— 这就是“粘性”的源码体现。


总结:一张时序图帮你串起来

text

子线程调用 postValue("test") │ ├─ ① 存入 mPendingData = "test" └─ ② 向主线程发消息 (Handler) (主线程空闲后) ↓ ③ 执行 Runnable:取出 mPendingData ④ 调用 setValue("test") ├─ mData = "test" ├─ mVersion++ (比如 0→1) └─ ⑤ dispatchingValue(null) │ ├─ 遍历 mObservers (包含你注册的监听) └─ ⑥ considerNotify(你的观察者) ├─ 检查生命周期 (STARTED?) ✅ ├─ 检查版本 (-1 < 1) ✅ └─ ⑦ 回调 onChanged("test") 🎉
http://www.jsqmd.com/news/1356328/

相关文章:

  • Godot引擎集成Spine Runtime:骨骼动画性能优化与实战指南
  • 3步实现批量水印自动化:semi-utils 免费工具终极指南
  • 2026年度优选昆明加油站膜结构棚厂家联系方式 - 装修教育财税推荐2026
  • 虚拟电厂多时间尺度调度与储能优化技术解析
  • LeetDown:5分钟学会如何免费降级iPhone 5/5s和iPad经典机型
  • 告别竞价排名,智搜GEO如何重塑企业获客逻辑
  • 重复率AI率双高怎么办?2026年5款双降工具打分
  • UE5.1内网离线迁移部署:从文件拷贝到环境配置的完整实践指南
  • Input Leap实战指南:跨平台键盘鼠标共享的高效解决方案
  • 为什么老板一定要建设营销型网站的目的详解以及SEO优化策略
  • 2026年宁波出租房装修挑选,认准宁波简优建筑装饰有限公司 - 起跑123
  • Nacos配置中心实战:Spring Boot微服务动态配置管理与避坑指南
  • 2026降AI率工具实测盘点:16款横向对比谁更值得选
  • 基于LLM与工具调用的终端AI代码智能体构建实践
  • 前端下拉框进阶实战:从基础到远程搜索与组件封装
  • 阿里P8+跨平台专项面经:Flutter引擎原理、Dart isolate、闲鱼架构、小程序容器
  • 《MC大战僵尸2》梦境世界8-10关攻略:资源管理与动态防御
  • 专科毕业论文AI工具清单:26年这几款够用
  • 2026年宁波流水线设备选哪家口碑好 锦拓仓储实力靠谱 - 起跑123
  • 探索BiliBili-UWP:重新定义Windows平台的B站观影体验
  • 论文AI查重率过高?专业降AI率工具与技巧全解析
  • 深入PostgreSQL内核算法:从MVCC到查询优化器的性能调优实战
  • 2026年宁波靠谱的工业气体供应商推荐,宁波百方气体值得选 - 起跑123
  • 2026年咨询竹鸡种苗养殖基地哪家好 不妨来实地了解 - 起跑123
  • 用了3年预测性维护系统,我总结出这8条经验
  • KES融合数据库:多模数据处理与性能优化实践
  • 2026降AI率网站实测,毕业季这些工具真能用
  • 专科毕业论文AI生成工具清单:26年这几款够用
  • Unity VR项目升级:解决sampler_CameraDepthTexture报错与渲染管线迁移
  • 终极指南:如何在Windows/Linux/macOS上搭建高性能任天堂Switch模拟器