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

Android RxJava框架分析:它的执行流程是如何的?它的线程是如何切换的?如何自定义RxJava操作符?

目录

  1. RxJava是什么?为什么使用。
  2. RxJava是如何使用的呢?
  3. RxJava如何和Retrofit一起使用。
  4. RxJava源码分析。
  • (1)他执行流程是如何的。
  • (2)map
  • (3)线程的切换。
  1. 如何自定义RxJava操作符?

一、RxJava是什么?为什么使用

RxJava 是一个基于 ​​响应式编程范式​​ 的库,用于通过​​观察者模式​​和​​链式操作符​​,简化异步、事件驱动、多线程数据流处理的开发。

简单来说,RxJava 就像是一个​​“流水线工厂”​​,专门处理需要等待的任务(比如网络请求、数据库查询、复杂计算等)。它能把这些任务串成一条流水线,每个环节处理完数据后,自动传给下一个环节,还能灵活控制任务在哪个线程执行(比如后台线程干活,主线程更新UI)。

1.1 为什么要使用RxJava呢?

接下来,我们看看不使用的问题,以网络请求为例

需求​​:按顺序做三件事(登录 → 查询订单 → 更新UI)。

​传统写法​​:​​“回调地狱”​​,层层嵌套,像俄罗斯套娃!

// 伪代码:传统嵌套回调(问题代码)
api.login(new Callback<User>() {@Overridepublic void onSuccess(User user) {api.getOrders(user.getId(), new Callback<List<Order>>() {@Overridepublic void onSuccess(List<Order> orders) {runOnUiThread(() -> {showOrders(orders); // 切主线程更新UI});}@Overridepublic void onFailure(Throwable error) {showError(error); // 每个回调都要处理错误!}});}@Overridepublic void onFailure(Throwable error) {showError(error);}
});

​问题总结​​:

  • ​代码缩进成“金字塔”​​,维护困难。
  • ​重复处理错误​​,每个回调都要写 onFailure
  • ​手动切换线程​​(如 runOnUiThread),容易遗漏。

​RxJava写法​​:

// RxJava 链式调用(解决方案)
api.rxLogin()                   // 1. 登录(被观察者).flatMap(user -> api.rxGetOrders(user.getId())) // 2. 查询订单(操作符).observeOn(AndroidSchedulers.mainThread()) // 3. 切到主线程.subscribe(orders -> showOrders(orders), // 4. 观察者消费数据error -> showError(error));   // 统一错误处理!

优势​​:
1️ ​​代码变“直线”​​,逻辑清晰。
2️ ​​统一错误处理​​,一个 onError 搞定所有。
3️ ​​自动线程切换​​,不用写 runOnUiThread


二、RxJava是如何使用的呢?

(1)添加依赖

implementation 'io.reactivex.rxjava3:rxjava:3.1.8'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.2' // Android 需要

(2)使用

Observable.just("你好").subscribe(new Observer<String>() {@Overridepublic void onSubscribe(@NonNull Disposable d) {Log.d(TAG, "onSubscribe: 订阅开始");}@Overridepublic void onNext(@NonNull String s) {Log.d(TAG, "onNext: 拿到事件"+s);}@Overridepublic void onError(@NonNull Throwable e) {Log.d(TAG, "onError: 错误事件");}@Overridepublic void onComplete() {Log.d(TAG, "onComplete: 事件完成");}});

在这里插入图片描述

解释一下执行流程:

(1)首先我们先需要知道几个角色:观察者( Observer),被观察者( Observable)。

(2)当被观察者发送出数据(调用just方法)你好的时候,那么观察者就会收到消息(subscribe方法就是订阅)。

(3) subscribe() 将观察者与被观察者连接,触发 Observable 开始发射数据,Observer接收并处理事件(数据、错误、完成信号)。

(4)Observer的 onSubscribe 方法订阅时立即调用​​(最先执行)。 通知观察者订阅已建立。

(5) onNext 方法Observable 发射数据时调用。

(6) onComplete() 方法Observable ​​正常完成数据发射​​后调用 。onError 反之。


三、RxJava如何和Retrofit一起使用

其实就是将Retrofit的响应结果交给RxJava来处理

3.1 发起一个请求

(1)我们需要在Retrofit这里配置 RxJava适配器

public class RetrofitClient {private static final String BASE_URL = "https://www.wanandroid.com/";private static Retrofit retrofit;public static WanAndroidService getService() {if (retrofit == null) {retrofit = new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()) // Gson 解析.addCallAdapterFactory(RxJava3CallAdapterFactory.create()) // RxJava 支持.build();}return retrofit.create(WanAndroidService.class);}
}

这行代码的作用是 ​​让 Retrofit 支持返回 RxJava 3 的响应式类型​​(如 ObservableFlowableSingle 等),使得网络请求的结果可以直接通过 RxJava 的流式操作符处理。

(2)在接口这里,我们也是使用Observable来接收。

public interface WanAndroidService {// 示例1:登录接口(POST)@FormUrlEncoded@POST("/user/login")Observable<ApiResponse<User>> login(@Field("username") String username,@Field("password") String password);// 示例2:获取首页文章列表(GET)@GET("/article/list/{page}/json")Observable<ApiResponse<List<Article>>> getHomeArticles(@Path("page") int page);
}

(3)调用接口

private void login(String username, String password) {WanAndroidService service = RetrofitClient.getService();service.login(username, password).subscribeOn(Schedulers.io()) // 在IO线程发起请求.observeOn(AndroidSchedulers.mainThread()) // 在主线程处理结果.subscribe(new Observer<ApiResponse<User>>() {@Overridepublic void onSubscribe(@NonNull Disposable d) {compositeDisposable.add(d); // 统一管理订阅Log.d(TAG, "onSubscribe: ");}@Overridepublic void onNext(@NonNull ApiResponse<User> response) {Log.d(TAG, "onNext: "+response);}@Overridepublic void onError(@NonNull Throwable e) {// 网络错误Log.d(TAG, "onError: ");}@Overridepublic void onComplete() {// 请求完成Log.d(TAG, "onComplete: ");}});
}

执行逻辑:

  1. service.login(username, password)​:
    通过 Retrofit 定义的接口发起网络请求,返回一个 Observable<ApiResponse<User>>。注意 ​​此时网络请求尚未执行​​,只是定义了数据源。
  2. .subscribeOn(Schedulers.io())​:
    指定 Observable 的工作线程为 ​​IO 线程​
  3. observeOn(AndroidSchedulers.mainThread())​:
    指定 Observer 的回调方法(onNextonErroronComplete)在 ​​主线程​​ 执行。
  4. .subscribe(Observer)​:
    订阅 Observable,触发网络请求执行,并绑定观察者处理结果。 ​​此时网络请求正式启动​​。

3.2 发起两个请求,网络嵌套

需求:先登录,登录成功后再获取用户的收藏列表。

// 获取收藏列表 (GET, 需要登录态)
@GET("/lg/collect/list/{page}/json")
Observable<CollectListResponse> getCollectList(@Path("page") int page
);
private void nestedNetworkRequest() {WanAndroidService service = RetrofitClient.getService();service.login("xxx", "xxx").flatMap(loginResponse -> {Log.d(TAG, "nestedNetworkRequest: "+loginResponse);// 登录成功后,获取收藏列表if (loginResponse.getErrorCode() == 0) {return service.getCollectList(0); // 第0页} else {return Observable.error(new Throwable("登录失败"));}}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<CollectListResponse>() {@Overridepublic void onSubscribe(@NonNull Disposable d) {}@Overridepublic void onNext(@NonNull CollectListResponse response) {Log.d(TAG, "onNext: "+response);}@Overridepublic void onError(@NonNull Throwable e) {Log.d(TAG, "onError: "+e.getMessage());}@Overridepublic void onComplete() {// 请求完成}});
}

flatMap方法是当 login 请求成功返回数据后才会调用。

在这里插入图片描述

四、源码分析:

4.1 先从Observer(观察者)开始

里面会有一个泛型,onNext也是使用这个泛型。

在这里插入图片描述

4.2 Observable(被观察者)

(1)调用create方法的时候,就会创建一个ObservableCreate
在这里插入图片描述

ObservableCreate里面,包裹了source,按照上面的例子,就类似于调用了我们的login方法,将要发送的请求包裹起来。那么包裹起来干嘛?因为他不是现在执行的,我们都知道,需要调用订阅,才会触发整个流程执行。

4.3 Observable的subscribe订阅过程

在这里插入图片描述

​订阅发生​​:调用 subscribe() 时,触发 subscribeActual

在这里插入图片描述

进来以后,我们就可以看到,先执行了我们的onSubscribe方法,然后再去执行source,source就是我们前面说的,将请求包裹起来的内容。

我们看一个非常原始的代码。
在这里插入图片描述

然后再里面调用了onNext,就是执行了观察者的onNext方法,然后执行onCompleter那么到这里,整个执行流程就结束了。

4.4 Map变换操作符原理

为什么map可以改变onnext的接收类型呢?我们继续看看。

在这里插入图片描述

可以看到,这里的类型就进行了转换。但是为什么观察者也跟着变了呢?
在这里插入图片描述

在这里的时候,就已经处理。 返回一个R类型的Observable实例,那么T也就变成了R。

4.5 异步线程切换:subscribeOn(Schedulers.io())

我们拿一个代码来进行分析。

service.login(username, password).subscribeOn(Schedulers.io()) // 在IO线程发起请求.observeOn(AndroidSchedulers.mainThread()) // 在主线程处理结果.subscribe(new Observer<ApiResponse<User>>() {@Overridepublic void onSubscribe(@NonNull Disposable d) {compositeDisposable.add(d); // 统一管理订阅Log.d(TAG, "onSubscribe: ");}...}
}

subscribeOn接收一个Scheduler参数,Scheduler类它封装了线程池和线程切换逻辑。

在这里插入图片描述

在这里插入图片描述

那么Schedulers.io()做了什么?Schedulers.io()​是一种策略,他会将内部的线程池配置成IO密集型的。我们会发现里面有很多种策略。

在这里插入图片描述

那么我们看看subscribeOn做了什么呢?他拿到线程池以后,做什么呢?先保存起来。我们先记住subscribeOn返回了一个ObservableSubscribeOn类
在这里插入图片描述

因为我们知道任务需要靠订阅方法才触发的,所以我们来看看ObservableSubscribeOn中的
subscribeActual订阅方法
在这里插入图片描述

scheduler.scheduleDirect

在这里插入图片描述

createWorker是一个抽象方法,因为前面我们配置的是Schedulers.io(),所以打开IoScheduler的createWorker
,然后会调用schedult方法执行现成。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

所以我们的任务就被异步线程执行了。

4.5 主线程切换

那么他如何从异步线程,又切换回主线程的?

.observeOn(AndroidSchedulers.mainThread())

AndroidSchedulers.mainThread()也是一个Scheduler,这里就不多介绍了。我们主要看看他返回的Scheduler

在这里插入图片描述

最终切换主线程,还是使用到了handler

在这里插入图片描述

我们记住HandlerScheduler类

在这里插入图片描述

我们来到observeOn方法

在这里插入图片描述

所以,然后最终交给了HandlerScheduler类来执行。

好了,到这里,源码分析就结束了。

那么我们看源码是为了什么?我们可以自定义RxJava操作符来玩玩,也会让我们对前面学习更加的通透理解。

五、自定义RxJava操作符

我们之定义RxJava操作符,并不是说我们自己实现Observable,而是继承他去实现一些功能。

我们看过just方法就知道,其实继承了Observable
在这里插入图片描述

在这里插入图片描述

然后重写subscribeActual方法,将value包裹进行处理,然后再调用观察者进行分发。

下面我们就写一个防抖操作符,帮助我们理解整个流程。

5.1 自定义防抖操作符

public class RxView {private final static String TAG = RxView.class.getSimpleName();// 我们自己的操作符 == 函数public static Observable<Object> clicks(View view) {return new ViewClickObservable(view);}}

public class ViewClickObservable extends Observable<Object> {private final View view;private static final Object EVENT = new Object();public ViewClickObservable(View view) {this.view = view;}@Overrideprotected void subscribeActual(Observer<? super Object> observer) {MyListener myListener = new MyListener(view, observer);//1.拿到view进行处理observer.onSubscribe(myListener);this.view.setOnClickListener(myListener);}// 拿到viewstatic final class MyListener implements View.OnClickListener, Disposable {private final View view;private Observer<Object> observer;private final AtomicBoolean isDisposable = new AtomicBoolean();public MyListener(View view, Observer<Object> observer) {this.view = view;this.observer = observer;}@Overridepublic void onClick(View v) {if (isDisposed() == false) {observer.onNext(EVENT);}}// 如果用调用了 中断@Overridepublic void dispose() {// 如果没有中断过,才有资格,   取消view.setOnClickListener(null);if (isDisposable.compareAndSet(false, true)) {// 主线程 很好的中断if (Looper.myLooper() == Looper.getMainLooper()) {view.setOnClickListener(null);} else { // 主线程,通过Handler的切换/*new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);view.setOnClickListener(null);}};*///放到主线程执行。AndroidSchedulers.mainThread().scheduleDirect(new Runnable() {@Overridepublic void run() {view.setOnClickListener(null);}});}}}@Overridepublic boolean isDisposed() {return isDisposable.get();}}
}

在这里插入图片描述

RxView.clicks(button).throttleFirst(2000, TimeUnit.MILLISECONDS).subscribe(new Consumer<Object>() {@Overridepublic void accept(Object o) throws Exception {}});}
}

执行流程:

  • 用户通过 RxView.clicks(view) 创建 ViewClickObservable
  • 调用 subscribe() 后触发 subscribeActual,创建 MyListener 并绑定到 View 的点击事件。
  • 点击事件触发 onClick,通过 Observer 发送 onNext 事件。然后Observeraccept方法就收到了事件(object)
  • 调用 dispose() 时移除 View 的点击监听。

相关文章:

Android RxJava框架分析:它的执行流程是如何的?它的线程是如何切换的?如何自定义RxJava操作符?

目录 RxJava是什么&#xff1f;为什么使用。RxJava是如何使用的呢&#xff1f;RxJava如何和Retrofit一起使用。RxJava源码分析。 &#xff08;1&#xff09;他执行流程是如何的。&#xff08;2&#xff09;map&#xff08;3&#xff09;线程的切换。 如何自定义RxJava操作符…...

MySQL及线程关于锁的面试题

目录 1.了解过 MySQL 死锁问题吗&#xff1f; 2.什么是线程死锁&#xff1f;死锁相关面试题 2.1 什么是死锁&#xff1a; 2.2 形成死锁的四个必要条件是什么&#xff1f; 2.3 如何避免线程死锁&#xff1f; 3. MySQL 怎么排查死锁问题&#xff1f; 4.Java线上死锁问题如…...

前端vue+elementplus实现上传通用组件

上传组件&#xff1a; <template><div class"upload-file"><el-uploadmultiple:action"uploadFileUrl":before-upload"handleBeforeUpload":file-list"fileList":limit"limit":on-error"handleUploadE…...

联合类型的逻辑或关系与类型保护

在 TypeScript 中&#xff0c;联合类型&#xff08;Union Types&#xff09;是一种强大的类型工具&#xff0c;它允许一个变量可以是几种不同类型中的一种。联合类型通过逻辑“或”关系&#xff08;|&#xff09;连接多个类型。这种类型的灵活性使得我们能够处理多样化的数据输…...

spring boot3.0自定义校验注解:文章状态校验示例

文章目录 Spring Boot 自定义校验注解&#xff1a;状态校验示例一、创建 State 注解步骤&#xff1a;1. 创建自定义注解&#xff1a;2. 实现校验逻辑&#xff1a; 二、 实现自定义校验步骤:1. 在实体类中使用自定义校验注解 State&#xff1a;2. 添加 State 注解&#xff1a; 总…...

神经网络是如何工作的

人工智能最核心的技术之一&#xff0c;就是神经网络&#xff08;Neural Networks&#xff09;。但很多初学者会觉得它是个黑盒&#xff1a;为什么神经网络能识别图片、翻译语言&#xff0c;甚至生成文章&#xff1f; 本文用图解最小代码实现的方式&#xff0c;带你深入理解&am…...

【工作记录】crmeb后端项目打开、运行

1、下载代码 1&#xff09;安装git 不再详述 2&#xff09;git拉代码 项目地址如下&#xff0c;在vscode-分支中拉代码 # 克隆项目 git clone https://gitee.com/ZhongBangKeJi/crmeb_java/ 截图如下是已经成功拉下来 注意安装对应版本 2、maven配置 安装配置见&#x…...

智能手表测试计划文档(软/硬件)

&#x1f4c4; 智能手表测试计划文档&#xff08;软/硬件&#xff09; 项目名称&#xff1a;Aurora Watch S1 文档编号&#xff1a;AW-S1-QA-TP-001 编制日期&#xff1a;2025-xx-xx 版本&#xff1a;V1.0 编写人&#xff1a;xxx&#xff08;测试主管&#xff09; 一、测试目标…...

DeepSeek:开启能源领域智能化变革新时代

目录 一、DeepSeek 与能源领域变革的邂逅1.1 DeepSeek 在人工智能领域的地位与特点1.2 能源行业面临的挑战与变革需求1.3 DeepSeek 在能源领域应用的重要性和意义 二、能源政策解读与科普新助手2.1 能源政策解读的深度变革2.2 能源科普的创新使者 三、能源项目可行性分析新利器…...

红黑树算法笔记(二)性能对比实验

文章目录 1. 实验目标2. 对比数据结构3. 性能指标3.1 时间性能指标3.2 空间性能指标3.3 其他性能指标 4. 测试场景4.1 数据集特性变化4.2 操作模式变化4.3 环境因素变化 5. 实验设计5.1 基准测试设计5.1.1 CRUD性能基准测试5.1.2 混合负载测试5.1.3 范围查询测试 5.2 特殊场景测…...

Nlog适配达梦数据库进行日志插入

前言 原来使用的是SQLServer数据库&#xff0c;使用Nlog很流畅&#xff0c;没有什么问题。现在有个新项目需要使用麒麟操作系统和达梦数据库&#xff0c;业务流程开发完成之后发现Nlog配置文件中把数据库连接内容修改之后不能执行插入操作。 原Nlog.config配置 <?xml ve…...

k8s监控方案实践(三):部署与配置Grafana可视化平台

k8s监控方案实践&#xff08;三&#xff09;&#xff1a;部署与配置Grafana可视化平台 文章目录 k8s监控方案实践&#xff08;三&#xff09;&#xff1a;部署与配置Grafana可视化平台一、Grafana简介1. 什么是Grafana&#xff1f;2. Grafana与Prometheus的关系3. Grafana应用场…...

嵌入式系统架构验证工具:AADL Inspector v1.10 全新升级

软件架构建模与早期验证是嵌入式应用的关键环节。架构分析与设计语言&#xff08;AADL&#xff09;是专为应用软件及执行平台架构模型设计的语言&#xff0c;兼具文本与图形化的双重特性。AADL Inspector是一款轻量级的独立工具&#xff1a; 核心处理能力包括 √ 支持处理AA…...

STM32-模电

目录 一、MOS管 二、二极管 三、IGBT 四、运算放大器 五、推挽、开漏、上拉电阻 一、MOS管 1. MOS简介 这里以nmos管为例&#xff0c;注意箭头方向。G门极/栅极&#xff0c;D漏极&#xff0c;S源极。 当给G通高电平时&#xff0c;灯泡点亮&#xff0c;给G通低电平时&a…...

华为云Flexus+DeepSeek征文|从开通到应用:华为云DeepSeek-V3/R1商用服务深度体验

前言 本文章主要讲述在华为云ModelArts Studio上 开通DeepSeek-V3/R1商用服务的流程&#xff0c;以及开通过程中的经验分享和使用感受帮我更多开发者&#xff0c;在华为云平台快速完成 DeepSeek-V3/R1商用服务的开通以及使用入门注意&#xff1a;避免测试过程中出现部署失败等问…...

鸿蒙NEXT开发动画案例5

1.创建空白项目 2.Page文件夹下面新建Spin.ets文件&#xff0c;代码如下&#xff1a; /*** TODO SpinKit动画组件 - Pulse 脉冲动画* author: CSDN—鸿蒙布道师* since: 2024/05/09*/ ComponentV2 export struct SpinFive {// 参数定义Require Param spinSize: number 48;Re…...

面试篇:Spring MVC

基础概念 什么是Spring MVC&#xff1f; Spring MVC 是 Spring Framework 提供的一个基于 Servlet 的 Web 框架&#xff0c;属于 MVC&#xff08;Model-View-Controller&#xff09;架构的一种实现。它通过 DispatcherServlet 作为前端控制器&#xff0c;对请求进行分发和调度…...

ctfshow——web入门351~356

SSRF没有出网的部分 web入门351 $ch curl_init($url); 作用&#xff1a;初始化一个 cURL 会话&#xff0c;并设置目标 URL。解释&#xff1a; curl_init($url) 创建一个新的 cURL 资源&#xff0c;并将其与 $url 关联。这里的 $url 是用户提供的&#xff0c;因此目标地址完全…...

C++中六个特殊成员函数的关系

C中六个特殊成员函数的关系 C11之后的版本每个类有六个特殊的成员函数&#xff0c;之所以特殊是因为它们可以在各种情况下由编译器自动提供&#xff1b; 默认构造函数、复制构造函数、复制赋值运算符、析构函数、移动构造函数、移动赋值运算符 关系规则&#xff1a; 1、如果…...

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】金融风控分析案例-10.1 风险数据清洗与特征工程

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 PostgreSQL金融风控分析案例&#xff1a;风险数据清洗与特征工程实战一、案例背景&#xff1a;金融风控数据处理需求二、风险数据清洗实战&#xff08;一&#xff09;缺失值…...

美女热舞混剪视频批量剪辑生产技术实践:智能处理与原创性提升方案解析

一、引言&#xff1a;短视频工业化生产的技术转型 在美女类短视频内容运营中&#xff0c;通过标准化技术流程实现「高质量、规模化」产出成为核心需求。本文结合实战经验&#xff0c;解析如何通过智能素材重组、AI 语音合成、动态元素叠加等技术手段&#xff0c;构建自动化生产…...

破局智算瓶颈:400G光模块如何重构AI时代的网络神经脉络

一、技术演进与市场需求双重驱动 在数字化转型浪潮下&#xff0c;全球互联网流量正以每年30%的复合增长率持续攀升。根据Dell’Oro Group最新报告&#xff0c;2023年400G光模块市场规模已突破15亿美元&#xff0c;预计2026年将占据数据中心光模块市场60%以上份额。这种爆发式增…...

python标准库--collections - 高性能数据结构在算法比赛的应用

目录 一、deque双端队列 1.头部删除元素popleft&#xff08;&#xff09; 2.BFS&#xff08;广度优先搜索&#xff09;优化 3.滑动窗口&#xff08;双指针&#xff09; 4.实现栈或队列 5. 双向遍历与操作 一、deque双端队列 特点&#xff1a;支持两端 O (1) 时间复杂度的…...

神经网络基础-从零开始搭建一个神经网络

一、什么是神经网络 人工神经网络(Articial Neural Network,简写为ANN)也称为神经网络(NN),是一种模仿生物神经网络和功能的计算模型,人脑可以看做是一个生物神经网络,由众多的神经元连接而成,各个神经元传递复杂的电信号,树突接收到输入信号,然后对信号进行处理,通…...

【Go】优化文件下载处理:从多级复制到零拷贝流式处理

在开发音频处理服务过程中&#xff0c;我们面临一个常见需求&#xff1a;从网络下载音频文件并保存到本地。这个看似简单的操作&#xff0c;实际上有很多优化空间。本文将分享一个逐步优化的过程&#xff0c;展示如何从一个基础实现逐步改进到高效的流式下载方案。 初始实现&a…...

Java 显式锁与 Condition 的使用详解

Java 显式锁与 Condition 的使用详解 在多线程编程中&#xff0c;线程间的协作与同步是核心问题。Java 提供了多种机制来实现线程同步&#xff0c;除了传统的 synchronized 关键字外&#xff0c;ReentrantLock 和 Condition 是更灵活且功能强大的替代方案。本文将详细介绍显式…...

android ViewModel liveData无法监听之多线程下activityViewModels不安全

我们一般的&#xff0c;会遇到liveData无法监听到结果&#xff0c;可能存在主要2种可能&#xff1a; liveData没有正确注册&#xff1b;liveData连续多次设置值&#xff0c;中间的值&#xff0c;会被丢弃&#xff0c;但最后一次是能监听到的。 但是我们容易忽略一种case&…...

#Redis黑马点评#(五)Redisson原理详解

目录 一 基于Redis的分布式锁优化 二 Redisson 1 实现步骤 2 Redisson可重入锁机制 3 Redisson可重试机制 4 Redisson超时释放机制 5 RedissonMultiLock解决主从一致性 三 trylock与lock两者有何区别 四 Redis优化秒杀 一 基于Redis的分布式锁优化 二 Redisson Redis…...

23.(vue3.x+vite)引入组件并动态切换(component)

让多个组件使用同一个挂载点,并动态切换,这就是动态组件 效果截图 A组件代码: <template><div><div>{{message }}</</...

VBA会被Python代替吗

VBA不会完全被Python取代、但Python在自动化、数据分析与跨平台开发等方面的优势使其越来越受欢迎、两者将长期并存且各具优势。 Python以其易于学习的语法、强大的开源生态系统和跨平台支持&#xff0c;逐渐成为自动化和数据分析领域的主流工具。然而&#xff0c;VBA依旧在Exc…...