Android--Jetpack--LiveData源码分析

时人不识凌云木,直待凌云始道高

一,基本使用

基本使用请看文章Android--Jetpack--LiveData-CSDN博客

二,MutableLiveData

首先说一下我们为什么要用MutableLiveData呢,来看看LiveData的源码:

public abstract class LiveData<T> {。。。。protected void postValue(T value) {boolean postTask;synchronized (mDataLock) {postTask = mPendingData == NOT_SET;mPendingData = value;}if (!postTask) {return;}ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);}@MainThreadprotected void setValue(T value) {assertMainThread("setValue");mVersion++;mData = value;dispatchingValue(null);}
}

setValue 和postValue 都是protected方法,外面拿不到

同时,这里的setValue使用了注解@MainThread 说明只能在主线程发送数据

再来看看MutableLiveData的源码:

public class MutableLiveData<T> extends LiveData<T> {/*** Creates a MutableLiveData initialized with the given {@code value}.** @param value initial value*/public MutableLiveData(T value) {super(value);}/*** Creates a MutableLiveData with no value assigned to it.*/public MutableLiveData() {super();}@Overridepublic void postValue(T value) {super.postValue(value);}@Overridepublic void setValue(T value) {super.setValue(value);}
}

这是它的全部代码,只是把postValue和setValue这两个方法公开了。其余的都是继承LiveData。

这就是我们为什么使用MutableLiveData的原因。

三,消息发送源码分析

setValue和postValue都是发送数据的,但是setValue只能在主线程发送数据,而postValue却不受线程限制。

我们来看看postValue的源码:

protected void postValue(T value) {boolean postTask;synchronized (mDataLock) {postTask = mPendingData == NOT_SET;mPendingData = value;}if (!postTask) {return;}ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}

通过ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);切换到了主线程,再来看看mPostValueRunnable:

private final Runnable mPostValueRunnable = new Runnable() {@SuppressWarnings("unchecked")@Overridepublic void run() {Object newValue;synchronized (mDataLock) {newValue = mPendingData;mPendingData = NOT_SET;}setValue((T) newValue);}
};

最终还是调用了setValue,所以说postValue只是多了一个线程切换的操作。

所以我们重点看下setValue的源码:

@MainThread
protected void setValue(T value) {assertMainThread("setValue");mVersion++;mData = value;dispatchingValue(null);
}

首先看看assertMainThread("setValue"):

static void assertMainThread(String methodName) {if (!ArchTaskExecutor.getInstance().isMainThread()) {throw new IllegalStateException("Cannot invoke " + methodName + " on a background"+ " thread");}
}

这个方法的作用就是检查是不是在主线程,不在主线程就抛异常。

mVersion++ 我们这里先记住这个值,待会再来分析。

mData =value 这就是一个赋值操作

然后我们再来看dispatchingValue(null)的源码:

void dispatchingValue(@Nullable ObserverWrapper initiator) {if (mDispatchingValue) {mDispatchInvalidated = true;return;}mDispatchingValue = true;do {mDispatchInvalidated = false;if (initiator != null) {considerNotify(initiator);initiator = null;} else {for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {considerNotify(iterator.next().getValue());if (mDispatchInvalidated) {break;}}}} while (mDispatchInvalidated);mDispatchingValue = false;
}

上面都是判断,主要来看这行代码:

for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {considerNotify(iterator.next().getValue());if (mDispatchInvalidated) {break;}
}

这里有个mObservers,我们来看看这是什么:

private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =new SafeIterableMap<>();

这是一个key为Obsercer,value为ObserverWrapper的map集合。

我们再来看看mObservers是在什么时候添加元素的:

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {assertMainThread("observe");if (owner.getLifecycle().getCurrentState() == DESTROYED) {// ignorereturn;}LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);if (existing != null && !existing.isAttachedTo(owner)) {throw new IllegalArgumentException("Cannot add the same observer"+ " with different lifecycles");}if (existing != null) {return;}owner.getLifecycle().addObserver(wrapper);
}

是在observe方法中添加的,先看看observe什么时候调用:

Observer observer =new Observer<String>() {@Overridepublic void onChanged(String o) {System.out.println("yz---"+o);txt.setText(o);}
};
//获取viewmodule
model =new ViewModelProvider(this).get(MyViewModel.class);
model.getCount().observe(this,observer);

在上篇使用的时候,我们会在Activity中这样调用observe,第一个参数是Activity,第二个参数是自己定义的观察者。

然后我们再回到observe方法中,首先检查了是不是在主线程,然后判断如果当前Activity的状态是不可用状态,就返回。如果是可用状态,就将Activity和观察者包装成wrapper,并且以观察者为key,wrapper为value添加进mObservers集合。如果这个集合里面不是空的并且不是重复的observer 就执行owner.getLifecycle().addObserver(wrapper);我们在Android--Jetpack--Lifecycle详解-CSDN博客Android--Jetpack--Lifecycle详解-CSDN博客Android--Jetpack--Lifecycle详解-CSDN博客中讲过,这个就会添加进观察者集合中。

了解了mObservers之后,我们再回到dispatchingValue(null)的源码:

void dispatchingValue(@Nullable ObserverWrapper initiator) {if (mDispatchingValue) {mDispatchInvalidated = true;return;}mDispatchingValue = true;do {mDispatchInvalidated = false;if (initiator != null) {considerNotify(initiator);initiator = null;} else {for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {considerNotify(iterator.next().getValue());if (mDispatchInvalidated) {break;}}}} while (mDispatchInvalidated);mDispatchingValue = false;
}

这里会执行considerNotify(iterator.next().getValue());继续看它的源码:

private void considerNotify(ObserverWrapper observer) {if (!observer.mActive) {return;}// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.//// we still first check observer.active to keep it as the entrance for events. So even if// the observer moved to an active state, if we've not received that event, we better not// notify for a more predictable notification order.if (!observer.shouldBeActive()) {observer.activeStateChanged(false);return;}if (observer.mLastVersion >= mVersion) {return;}observer.mLastVersion = mVersion;observer.mObserver.onChanged((T) mData);
}

将上面我们包装的ObserverWrapper传了进来。

首先判断这个观察者是不激活状态,就返回不执行

然后就到了这句代码:

if (observer.mLastVersion >= mVersion) {return;
}

mVersion刚才出现过,我们看看mVersion:

public LiveData() {mData = NOT_SET;mVersion = START_VERSION;
}

在我们创建new MutableLiveData<>()的时候,会给mVersion赋值START_VERSION=-1

然后我们setValue的时候mVersion会+1.

之后就会走到considerNotify(iterator.next().getValue())方法中。

observer.mLastVersion的初始值:

static final int START_VERSION = -1;
int mLastVersion = START_VERSION;

所以,我们setValue的时候,会走到dispatchingValue方法中,然后会走到considerNotify中,接下来就到了 observer.mObserver.onChanged((T) mData)方法,这时就会回调observer的api把消息发送出来了。

四,数据倒灌和粘性问题

正常我们的执行顺序是:new LiveData-->绑定observer-->setValue执行onChanged

但是当我们跨Activity的时候,可能执行顺序是这样的:

new LiveData-->setValue执行onChanged-->绑定observer

这样会有什么问题呢?

首先,正常的执行流程上面我们已经分析过了。

下面 我们来看看不正常的流程:

new LiveData的时候:

public LiveData() {mData = NOT_SET;mVersion = START_VERSION;
}

mVersion=-1

然后执行setValue:

@MainThread
protected void setValue(T value) {assertMainThread("setValue");mVersion++;mData = value;dispatchingValue(null);
}

mVersion =0

这时不会走到:

private void considerNotify(ObserverWrapper observer) {if (!observer.mActive) {return;}// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.//// we still first check observer.active to keep it as the entrance for events. So even if// the observer moved to an active state, if we've not received that event, we better not// notify for a more predictable notification order.if (!observer.shouldBeActive()) {observer.activeStateChanged(false);return;}if (observer.mLastVersion >= mVersion) {return;}observer.mLastVersion = mVersion;observer.mObserver.onChanged((T) mData);
}

因为还没绑定observer:

void dispatchingValue(@Nullable ObserverWrapper initiator) {if (mDispatchingValue) {mDispatchInvalidated = true;return;}mDispatchingValue = true;do {mDispatchInvalidated = false;if (initiator != null) {considerNotify(initiator);initiator = null;} else {for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {considerNotify(iterator.next().getValue());if (mDispatchInvalidated) {break;}}}} while (mDispatchInvalidated);mDispatchingValue = false;
}

所以此时observer.mLastVersion =-1

但是因为是一个while循环,所以当绑定observer时,然后会走到:

private void considerNotify(ObserverWrapper observer) {if (!observer.mActive) {return;}// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.//// we still first check observer.active to keep it as the entrance for events. So even if// the observer moved to an active state, if we've not received that event, we better not// notify for a more predictable notification order.if (!observer.shouldBeActive()) {observer.activeStateChanged(false);return;}if (observer.mLastVersion >= mVersion) {return;}observer.mLastVersion = mVersion;observer.mObserver.onChanged((T) mData);
}

然后我们就会收到消息。

所以就造成了我们订阅之前的消息也收到了。

五,解决方案

public class MyLiveData<T> extends MutableLiveData<T> {private int mVersion = 0;//被观察者的版本private int observerVersion = 0;//观察者的版本@Overridepublic void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {//每次订阅的时候,先把版本同步observerVersion = mVersion;super.observe(owner, new Observer<T>() {@Overridepublic void onChanged(T t) {if (mVersion != observerVersion) {observer.onChanged(t);}}});}@MainThreadpublic void setValue(T value) {mVersion++;super.setValue(value);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/205873.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[足式机器人]Part2 Dr. CAN学习笔记-数学基础Ch0-7欧拉公式的证明

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-数学基础Ch0-7欧拉公式的证明 e i θ cos ⁡ θ sin ⁡ θ i , i − 1 e^{i\theta}\cos \theta \sin \theta i,i\sqrt{-1} eiθcosθsinθi,i−1 ​ 证明&#xff1a; f ( θ ) e i θ cos …

论文阅读:LSeg: LANGUAGE-DRIVEN SEMANTIC SEGMENTATION

可以直接bryanyzhu的讲解&#xff1a;CLIP 改进工作串讲&#xff08;上&#xff09;【论文精读42】_哔哩哔哩_bilibili 这里是详细的翻译工作 原文链接 https://arxiv.org/pdf/2201.03546.pdf ICLR 2022 0、ABSTRACT 我们提出了一种新的语言驱动的语义图像分割模型LSeg。…

【webpack】应用篇

基础应用 代码分离常用的代码分离方法方法一&#xff1a;配置入口节点方法二&#xff1a;防止重复方法三&#xff1a;动态导入 缓存原因解决思路 缓存第三方库原因解决思路 将所有js文件单独存放文件夹拆分开发环境和生产环境配置公共路径环境变量和区分环境代码压缩 拆分配置文…

【Python】np.save()和np.load()函数详解和示例

本文通过函数原理和运行示例&#xff0c;对np.save()和np.load()函数进行详解&#xff0c;以帮助大家理解和使用。 更多Numpy函数详解和示例&#xff0c;可参考 【Python】Numpy库近50个常用函数详解和示例&#xff0c;可作为工具手册使用 目录 np.save &#xff08;&#xff…

云架构的思考3--云上开发

目录 1 DevOps--简单灵活性高2 服务化&#xff08;微服务&#xff09;--弹性&#xff08;可扩展&#xff09;、按需自主服务3 无状态&#xff08;Serverless&#xff09;--弹性&#xff08;可扩展&#xff09;4 日志--安全5 配置中心--安全6 设计模式6.1 使用“适配器模式”调用…

Go--协程

协程 协程是Go语言最大的特色之一。 1、协程的概念 协程并不是Go发明的概念&#xff0c;支持协程的变成语言有很多。Go在语言层面直接提供对协程的支持称为goroutine。 1.1 基本概念 进程 进程是应用程序启动的实例&#xff0c;每个进程都有独立的内存空间&#xff0c;不同…

nodejs微信小程序+python+PHP的智能停车系统-计算机毕业设计推荐django

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

Glide系列-活动缓存和内存缓存

1.活动缓存数据结构用的HashMap final class ActiveResources {VisibleForTesting final Map<Key, ResourceWeakReference> activeEngineResources new HashMap<>(); } 2.内存缓存LinkedHashMap public class LruCache<T, Y> {private final…

解决思维题的一些自我总结

目录 常见思维题类型 排序 区间问题 01串串 字符串串 位运算 gcd 与 lcm 质数相关 二元组 常见思维题类型 思维题很多都可以说是贪心、但贪心种类很多&#xff0c;具体怎么贪&#xff0c;重要的还是在于积累经验吧...有些东西也很难总结&#xff0c;以下算是我的碎碎念…

Next.js 中的中间件

Next.js 中的中间件 Next.js 中的中间件是一个功能强大的工具&#xff0c;允许开发人员拦截、修改和控制应用程序中的请求和响应流。无论我们是构建服务器渲染的网站还是成熟的 Web 应用程序&#xff0c;了解如何有效使用中间件都可以显着增强项目进出的数据流。本文将从基础知…

Thymeleaf生成pdf表格合并单元格描边不显示

生成pdf后左侧第一列的右描边不显示&#xff0c;但是html显示正常 显示异常时描边的写法 cellpadding“0” cellspacing“0” &#xff0c;td,th描边 .self-table{border:1px solid #000;border-collapse: collapse;width:100%}.self-table th{font-size:12px;border:1px sol…

el-select的多选multible带全选组件二次封装(vue2,elementUI)

1.需求 Select 选择器 多选需要增加 全选 和 取消全选 功能&#xff0c;前端框架为vue2&#xff0c;UI组件为elementUI。 2. 代码 html部分 <template><el-tooltip effect"dark" :disabled"defaultValue.length < 0" :content"defaul…

GO设计模式——7、适配器模式(结构型)

目录 适配器模式&#xff08;Adapter Pattern&#xff09; 优缺点 使用场景 注意事项 代码实现 适配器模式&#xff08;Adapter Pattern&#xff09; 适配器模式&#xff08;Adapter Pattern&#xff09;是作为两个不兼容的接口之间的桥梁。将一个类的接口转化为客户希望的…

dockerfile简单实践部署(jenkins,wordpress)

实现部署jenkins的流程 配置java环境&#xff0c;导入jenkins包&#xff0c;运行命令 java -jar jenkins包&#xff0c;这里为了减少进入jenkins的web端安装插件&#xff0c;将插件提前部署到容器内。 制作dockerfile 创建镜像所在的文件夹和Dockerfile文件 mkdir /test cd …

如何优雅使用 vue-html2pdf 插件生成pdf报表

使用 vue-html2pdf 插件 业务背景&#xff0c;老板想要一份能征服客户的pdf报表&#xff0c;传统的pdf要手撕&#xff0c;企业中确实有点耗费时间&#xff0c;于是github上面看到开源的这个插件就…废话不多说&#xff0c;直接上教程 1.使用下面命令安装 vue-html2pdf npm i…

Vue3项目调用腾讯地图服务(地址解析 地址转坐标)及使用axios的跨域问题

一,需求 根据传入的文本地址 将其转换为坐标 显示地图点位在腾讯地图上 二,使用axios发送请求 import axios from axios; //引入axiosaxios({url:https://apis.map.qq.com/ws/geocoder/v1,method:get//参数 地址和key值}).then((data)>{console.log(data)});但是使用完报跨…

第二十一章总结博客

网络程序设计基础 局域网与互联网 为了实现两台计算机的通信&#xff0c;必须用一个网络线路连接两台计算机。如下图所示 网络协议 1.IP协议 IP是Internet Protocol的简称&#xff0c;是一种网络协议。Internet 网络采用的协议是TCP/IP协议&#xff0c;其全称是Transmission …

阿里云 ACR 制品中心 AI/大数据镜像专场上新推荐榜

今天&#xff0c;AI 领域的快速发展不仅需要算法的突破&#xff0c;也需要工程的创新。随着容器技术和服务在企业的应用程度不断加深&#xff0c;企业对于容器的使用也越来越多地从在线业务逐渐向 AI、大数据类型的工作负载发展。同时&#xff0c;开发人员在考虑如何通过云原生…

MQTT源码分析

目录 MQTT源码分析 1. MQTT客户端功能 2. 客户端软件如何实现 3. 程序分层 4. 情景分析 4.1 连接服务器 4.2 创建线程 4.3 发布消息 4.4 最复杂&#xff1a;订阅消息 MQTT源码分析 分析源码&#xff1a;mqttclient\test\emqx\test.c 参考资料&#xff1a; kawaii-mqt…

IntelliJ IDEA 2023.3 最新变化

关键亮点 AI Assistant 预览阶段结束 全面推出 Ultimate JetBrains AI Assistant 现已全面推出&#xff0c;搭载大量新功能和改进&#xff0c;助力提高您在 JetBrains IDE 中的工作效率。 最新更新包括编辑器中增强的直接代码生成、无需复制代码即可回答项目相关查询的上下文…