RxJava 全解析:从原理到 Android 实战

在 Android 开发中,异步任务处理是绕不开的核心场景 —— 网络请求、数据库操作、文件读写等都需要在后台执行,而结果需回调到主线程更新 UI。传统的 “Handler+Thread” 或 AsyncTask 不仅代码冗余,还容易陷入 “回调地狱”(嵌套回调导致代码可读性差)。RxJava 作为一款基于响应式编程思想的异步框架,通过 “链式调用” 和 “操作符” 完美解决了这些问题,成为 Android 开发者的必备工具。本文将从 RxJava 的核心原理、核心组件到 Android 实战,全面讲解 RxJava 的使用。

一、RxJava 核心概念:什么是响应式编程?

1.1 响应式编程与 RxJava

响应式编程(Reactive Programming)是一种 “以数据流和变化传播为核心” 的编程范式。简单来说,就是将所有操作抽象为 “数据流”,数据的产生、转换、消费都通过数据流传递,当数据变化时,依赖该数据的操作会自动响应。

RxJava(Reactive Extensions for Java)是响应式编程在 Java 平台的实现,其核心思想是:

  • 以观察者模式为基础:通过 “被观察者(Observable)” 产生数据,“观察者(Observer)” 消费数据;
  • 支持链式操作:数据从产生到消费的过程中,可通过 “操作符(Operator)” 进行转换(如过滤、映射、线程切换);
  • 异步非阻塞:默认在指定线程执行任务,避免阻塞主线程。

形象比喻:RxJava 就像 “水管系统”—— 被观察者是 “水源”,操作符是 “水管中的过滤器 / 转换器”,观察者是 “水龙头”。水流(数据)从水源出发,经过过滤、转换后,最终从水龙头流出被使用。

1.2 RxJava 解决的核心问题

对比传统异步方式,RxJava 的优势体现在三个方面:

1.消除回调地狱

传统嵌套回调(如 “网络请求 1→网络请求 2→更新 UI”)的代码如下:

// 传统回调嵌套(回调地狱)
api.requestData1(new Callback() {@Overridepublic void onSuccess(Data1 data1) {api.requestData2(data1.getId(), new Callback() {@Overridepublic void onSuccess(Data2 data2) {runOnUiThread(() -> updateUI(data2));}@Overridepublic void onFailure(Throwable e) { ... }});}@Overridepublic void onFailure(Throwable e) { ... }
});

用 RxJava 实现的链式调用:

// RxJava链式调用(无嵌套)
api.rxRequestData1() // 第一步:请求数据1.flatMap(data1 -> api.rxRequestData2(data1.getId())) // 第二步:用数据1请求数据2.observeOn(AndroidSchedulers.mainThread()) // 切换到主线程.subscribe(data2 -> updateUI(data2), // 成功回调e -> handleError(e) // 错误回调);

2.线程切换简化

传统方式需通过 Handler 手动切换线程,RxJava 通过subscribeOn和observeOn两个操作符即可指定 “任务执行线程” 和 “回调线程”,无需手动处理线程切换。

3.数据处理标准化

无论是网络请求、数据库查询还是事件监听,都可抽象为 Observable,通过统一的操作符进行处理(如过滤空数据、转换数据格式),降低代码耦合。

二、RxJava 核心组件:Observable 与 Observer

RxJava 的核心组件包括 “被观察者(Observable)”“观察者(Observer)”“订阅(Subscribe)”“操作符(Operator)”,这四个组件构成了 RxJava 的基本骨架。

2.1 被观察者(Observable):数据的产生者

Observable 是数据的 “源头”,负责产生数据(可以是单个数据、多个数据或一个错误)。其生命周期包含三个关键事件:

  • onNext(T t):发送一条数据(可多次调用);
  • onError(Throwable e):发送一个错误(仅一次,发送后终止);
  • onComplete():表示数据发送完成(仅一次,发送后终止)。

简单示例:创建一个发送 3 个整数的 Observable

// 创建被观察者:发送1、2、3三个数据,然后完成
Observable<Integer> observable = Observable.create(emitter -> {emitter.onNext(1);emitter.onNext(2);emitter.onNext(3);emitter.onComplete(); // 数据发送完成
});

除了create,RxJava 还提供了便捷的创建方法:

  • Observable.just(t1, t2, ...):发送指定的单个 / 多个数据;
  • Observable.fromIterable(iterable):发送集合中的数据;
  • Observable.timer(delay, unit):延迟指定时间后发送一个 0L;
  • Observable.interval(period, unit):每隔指定时间发送一个递增的 Long 值(如定时任务)。

示例:用just创建 Observable

Observable<String> observable = Observable.just("A", "B", "C"); // 发送A、B、C

2.2 观察者(Observer):数据的消费者

Observer 是数据的 “消费者”,负责接收 Observable 发送的事件(onNext/onError/onComplete)并处理。RxJava 中有两种常用的观察者接口:

  1. Observer:完整的观察者接口,需实现三个方法:
    Observer<String> observer = new Observer<String>() {@Overridepublic void onSubscribe(Disposable d) {// 订阅时调用(可保存Disposable用于取消订阅)mDisposable = d;}@Overridepublic void onNext(String s) {// 接收数据(对应Observable的onNext)Log.d("RxJava", "收到数据:" + s);}@Overridepublic void onError(Throwable e) {// 接收错误(对应Observable的onError)Log.e("RxJava", "发生错误:" + e.getMessage());}@Overridepublic void onComplete() {// 接收完成通知(对应Observable的onComplete)Log.d("RxJava", "数据接收完成");}
    };

  2. Consumer:简化的观察者(仅关注数据和错误),适合只需要处理 onNext 和 onError 的场景:
    // 只处理正常数据
    Consumer<String> onNext = s -> Log.d("RxJava", "收到数据:" + s);
    // 处理错误
    Consumer<Throwable> onError = e -> Log.e("RxJava", "错误:" + e.getMessage());

2.3 订阅(Subscribe):连接 Observable 与 Observer

Observable 和 Observer 本身是独立的,需通过 “订阅(subscribe)” 建立关联。调用observable.subscribe(observer)后,Observable 开始发送数据,Observer 开始接收数据。

订阅示例

// Observable与Observer建立订阅关系
Disposable disposable = observable.subscribe(s -> Log.d("RxJava", "收到:" + s), // onNexte -> Log.e("RxJava", "错误:" + e.getMessage()), // onError() -> Log.d("RxJava", "完成"), // onCompleted -> mDisposable = d // onSubscribe(可选)
);

关键对象:Disposable

subscribe方法返回的Disposable(可理解为 “开关”)用于取消订阅:

  • disposable.dispose():切断 Observable 与 Observer 的连接,Observer 不再接收数据;
  • disposable.isDisposed():判断是否已取消订阅。

为什么需要取消订阅?

若页面销毁后,Observable 仍在发送数据并回调 UI,会导致内存泄漏(Observer 持有 Activity 引用)。需在onDestroy中调用dispose():

@Override
protected void onDestroy() {super.onDestroy();if (mDisposable != null && !mDisposable.isDisposed()) {mDisposable.dispose(); // 取消订阅,避免内存泄漏}
}

2.4 操作符(Operator):数据的转换器

操作符是 RxJava 的 “灵魂”,用于在数据从 Observable 到 Observer 的过程中进行转换、过滤、组合等操作。RxJava 提供了上百种操作符,按功能可分为几类核心操作符。

(1)转换操作符:修改数据格式
  • map:将一种类型的数据转换为另一种类型(一对一转换)
    // 将Integer转换为String(1→"Number:1")
    Observable.just(1, 2, 3).map(number -> "Number: " + number) // 转换逻辑.subscribe(s -> Log.d("RxJava", s)); // 输出:Number: 1、Number: 2、Number: 3

  • flatMap:将一个数据转换为另一个 Observable(一对多转换,用于嵌套请求)
    // 模拟:根据用户ID获取用户信息,再根据用户信息获取订单列表
    Observable.just(1001) // 用户ID.flatMap(userId -> getUserInfo(userId)) // 转换为“用户信息Observable”.flatMap(userInfo -> getOrderList(userInfo.getUserId())) // 转换为“订单列表Observable”.subscribe(orders -> updateOrderUI(orders));

(2)过滤操作符:筛选数据
  • filter:按条件筛选数据(保留符合条件的数据)
    // 筛选偶数
    Observable.just(1, 2, 3, 4, 5).filter(number -> number % 2 == 0) // 条件:偶数.subscribe(number -> Log.d("RxJava", "偶数:" + number)); // 输出:2、4

  • take:只取前 N 个数据
    // 只取前2个数据
    Observable.just("A", "B", "C", "D").take(2).subscribe(s -> Log.d("RxJava", s)); // 输出:A、B

(3)线程切换操作符:指定执行线程

Android 开发中最常用的操作符,用于指定 “任务执行线程” 和 “回调线程”:

  • subscribeOn:指定 Observable 发送数据的线程(仅第一次调用有效);
  • observeOn:指定 Observer 接收数据的线程(可多次调用,每次调用切换后续线程)。

RxJava 通过Schedulers提供常用线程:

  • Schedulers.io():IO 密集型线程池(网络请求、文件读写,线程数无上限);
  • Schedulers.computation():CPU 密集型线程池(数据计算,线程数 = CPU 核心数);
  • AndroidSchedulers.mainThread():Android 主线程(需引入 RxAndroid 库)。

示例:网络请求在 IO 线程执行,结果在主线程回调

// 需添加RxAndroid依赖(提供mainThread())
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'// 线程切换示例
Observable.fromCallable(() -> {// 耗时操作(在IO线程执行)return fetchDataFromNetwork(); // 网络请求
})
.subscribeOn(Schedulers.io()) // 指定发送数据的线程(IO线程)
.observeOn(AndroidSchedulers.mainThread()) // 指定接收数据的线程(主线程)
.subscribe(data -> updateUI(data), // 在主线程更新UIe -> showError(e)
);
(4)组合操作符:合并多个 Observable
  • concat:按顺序合并多个 Observable(前一个完成后再执行下一个)
    Observable<String> observable1 = Observable.just("A", "B");
    Observable<String> observable2 = Observable.just("C", "D");Observable.concat(observable1, observable2).subscribe(s -> Log.d("RxJava", s)); // 输出:A、B、C、D

  • zip:将多个 Observable 的对应数据合并为一个新数据(如合并两个网络请求的结果)
    Observable<Integer> observable1 = Observable.just(1, 2);
    Observable<String> observable2 = Observable.just("A", "B");Observable.zip(observable1,observable2,(num, str) -> num + str // 合并逻辑:1+"A"→"1A",2+"B"→"2B"
    ).subscribe(s -> Log.d("RxJava", s)); // 输出:1A、2B

三、RxJava 在 Android 中的典型应用场景

RxJava 在 Android 开发中的核心价值是 “简化异步任务 + 线程切换”,以下是几个高频场景及实现。

3.1 网络请求 + UI 更新

场景:调用接口获取数据,在主线程更新 UI(避免手动线程切换)。

示例(结合 Retrofit,Retrofit 原生支持返回 Observable):

// 1. 定义Retrofit接口(返回Observable)
public interface ApiService {@GET("user/{id}")Observable<User> getUserInfo(@Path("id") String userId);
}// 2. 创建Retrofit实例
ApiService api = new Retrofit.Builder().baseUrl("https://api.example.com/").addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJava3CallAdapterFactory.create()) // 支持RxJava.build().create(ApiService.class);// 3. 发起请求并处理结果
Disposable disposable = api.getUserInfo("1001").subscribeOn(Schedulers.io()) // 网络请求在IO线程.observeOn(AndroidSchedulers.mainThread()) // 回调在主线程.subscribe(user -> {// 更新UItvName.setText(user.getName());tvAge.setText(String.valueOf(user.getAge()));},e -> {// 处理错误(如网络异常)Toast.makeText(this, "请求失败", Toast.LENGTH_SHORT).show();});// 4. 页面销毁时取消订阅
@Override
protected void onDestroy() {super.onDestroy();if (disposable != null && !disposable.isDisposed()) {disposable.dispose();}
}

3.2 数据库操作 + 数据转换

场景:从数据库查询数据,过滤无效数据后显示(用操作符简化数据处理)。

示例(结合 Room,Room 支持返回 Observable):

// 1. Room实体类
@Entity
public class User {@PrimaryKeypublic String id;public String name;public int age;
}// 2. Room DAO(返回Observable)
@Dao
public interface UserDao {@Query("SELECT * FROM user")Observable<List<User>> getAllUsers();
}// 3. 查询并过滤数据(只显示成年人)
Disposable disposable = userDao.getAllUsers().subscribeOn(Schedulers.io()) // 数据库操作在IO线程.map(users -> {// 过滤年龄≥18的用户(map转换)List<User> adults = new ArrayList<>();for (User user : users) {if (user.age >= 18) {adults.add(user);}}return adults;}).observeOn(AndroidSchedulers.mainThread()) // 主线程更新列表.subscribe(adults -> userAdapter.setData(adults),e -> Log.e("DB", "查询失败:" + e.getMessage()));

3.3 定时任务 + 周期性操作

场景:实现倒计时(如验证码倒计时 60 秒)。

示例

// 倒计时60秒(从60到0)
Disposable disposable = Observable.interval(0, 1, TimeUnit.SECONDS) // 立即执行,每秒一次.take(61) // 只取61个数据(0-60).map(count -> 60 - count) // 转换为倒计时(60,59,...,0).observeOn(AndroidSchedulers.mainThread()).subscribe(second -> {// 更新按钮文字btnCode.setText(second + "秒后重新发送");btnCode.setEnabled(second == 0); // 倒计时结束后可点击},e -> Log.e("Countdown", "错误:" + e.getMessage()));

3.4 合并多个请求结果

场景:需同时调用两个接口,合并结果后显示(如获取用户信息 + 用户订单)。

示例(用 zip 合并两个请求):

// 1. 两个接口请求
Observable<User> userObservable = api.getUserInfo("1001");
Observable<List<Order>> ordersObservable = api.getOrderList("1001");// 2. 合并结果
Disposable disposable = Observable.zip(userObservable,ordersObservable,(user, orders) -> new UserWithOrders(user, orders) // 合并为新对象
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(userWithOrders -> {// 显示用户信息和订单showUserInfo(userWithOrders.user);showOrders(userWithOrders.orders);},e -> Toast.makeText(this, "请求失败", Toast.LENGTH_SHORT).show()
);

四、RxJava 高级技巧与注意事项

4.1 避免内存泄漏:规范管理 Disposable

内存泄漏是 RxJava 最常见的问题,根本原因是 “Observer 持有 Activity/Fragment 引用,且未取消订阅”。解决方法:

  1. 使用 CompositeDisposable 管理多个 Disposable

当有多个订阅时,用CompositeDisposable统一管理:

private CompositeDisposable mCompositeDisposable = new CompositeDisposable();// 添加订阅
mCompositeDisposable.add(disposable1);
mCompositeDisposable.add(disposable2);// 取消所有订阅(在onDestroy中)
@Override
protected void onDestroy() {super.onDestroy();mCompositeDisposable.dispose(); // 一次性取消所有订阅
}
  1. 使用弱引用(WeakReference)

在 Observer 中若需引用 Activity,用弱引用避免强持有:

Observer<User> observer = new Observer<User>() {private WeakReference<MainActivity> activityRef;public Observer(MainActivity activity) {activityRef = new WeakReference<>(activity);}@Overridepublic void onNext(User user) {MainActivity activity = activityRef.get();if (activity != null && !activity.isFinishing()) {activity.updateUI(user); // 仅当Activity有效时更新}}// ...其他方法
};

4.2 错误处理:全局统一处理异常

RxJava 中若未处理onError,会导致程序崩溃。可通过onErrorResumeNext或全局异常处理器统一处理:

// 局部错误处理(返回默认数据)
Observable.just("1001").flatMap(id -> api.getUserInfo(id).onErrorResumeNext(throwable -> {// 发生错误时返回默认用户return Observable.just(new User("默认用户", 0));})).subscribe(...);// 全局错误处理(通过Transformer)
public <T> ObservableTransformer<T, T> handleError() {return upstream -> upstream.onErrorResumeNext(throwable -> {Log.e("GlobalError", "错误:" + throwable.getMessage());return Observable.empty(); // 发生错误时发送空数据});
}// 使用全局处理器
api.getUserInfo("1001").compose(handleError()) // 应用全局错误处理.subscribe(...);

4.3 操作符滥用:避免过度链式调用

虽然链式调用简洁,但过度使用操作符会导致:

  • 性能损耗:每个操作符都会创建新的 Observable,增加内存开销;
  • 可读性下降:过长的链式调用(如 10 个以上操作符)难以调试。

建议

  • 合并重复操作(如多个 map 可合并为一个);
  • 复杂转换逻辑提取为单独方法;
  • 避免不必要的操作符(如无需转换时不使用 map)。

4.4 RxJava3 的变化

目前 RxJava 已发展到 RxJava3,相比 RxJava2 的主要变化:

  • 移除Observable.OnSubscribe,改用ObservableSource;
  • 强化空安全(禁止发送 null 值,否则抛出异常);
  • 操作符命名更规范(如flatMapIterable替代flatMap的部分功能)。

建议直接使用 RxJava3,避免兼容旧版本问题。

五、RxJava 与其他框架的对比

框架

优势

劣势

适用场景

RxJava

操作符丰富,灵活度高,支持复杂数据处理

学习成本高,依赖较多

复杂异步场景(多请求合并、数据转换)

Kotlin 协程

语言级支持,轻量级,无额外依赖

缺乏操作符,复杂转换需手动实现

简单异步任务,Kotlin 项目

LiveData

生命周期感知,自动取消订阅

操作符少,不支持复杂转换

数据与 UI 绑定(配合 ViewModel)

最佳实践

  • 简单场景(如单一网络请求):Kotlin 协程更简洁;
  • 复杂场景(多请求合并、数据过滤):RxJava 更高效;
  • UI 数据监听:LiveData(或 RxJava+Lifecycle)。

六、总结

RxJava 的核心价值在于将异步任务 “数据流化”,通过观察者模式和操作符简化数据的产生、转换、消费流程。其在 Android 开发中的核心应用是:

  • 替代 Handler/AsyncTask:用subscribeOn和observeOn简化线程切换;
  • 消除回调地狱:通过 flatMap 等操作符将嵌套回调转为链式调用;
  • 统一数据处理:用操作符实现数据过滤、转换、合并,代码更简洁。

学习 RxJava 的关键是 “理解观察者模式” 和 “掌握核心操作符”,而非死记硬背所有操作符。实际开发中,应根据场景选择合适的操作符,避免过度设计。同时,务必注意 “取消订阅” 以防止内存泄漏 —— 这是 RxJava 使用的第一准则。

掌握 RxJava 后,你会发现异步任务处理从 “繁琐的回调嵌套” 变成 “流畅的链式调用”,代码可读性和可维护性将大幅提升。

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

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

相关文章

OpenCV 官翻7 - 对象检测

文章目录ArUco 标记检测标记与字典标记物创建标记检测姿态估计选择字典预定义字典自动生成字典手动定义字典检测器参数阈值处理adaptiveThreshConstant轮廓过滤minMarkerPerimeterRate 与 maxMarkerPerimeterRatepolygonalApproxAccuracyRateminCornerDistanceRateminMarkerDis…

【Oracle】ORACLE OMF说明

ORACLE OMF (Oracle Managed Files) 是 Oracle 数据库提供的一项自动化文件管理功能。它的核心目的是简化数据库管理员&#xff08;DBA&#xff09;对数据库底层操作系统文件的管理工作。 以下是 OMF 的关键要点&#xff1a; 核心功能&#xff1a;自动命名和定位文件 在创建数据…

408考研逐题详解:2010年第35题——RIP协议

2010年第35题 某自治系统内采用 RIP 协议&#xff0c;若该自治系统内的路由器 R1 收到其邻居路由器 R2 的距离矢量&#xff0c;距离矢量中包含信息 <net1, 16>&#xff0c;则能得出的结论是&#xff08; &#xff09; A. R2 可以经过 R1 到达 net1&#xff0c;跳数为17 …

http与https的主要区别是什么?

什么是HTTP&#xff1f; HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是互联网上应用最为广泛的一种网络协议。它构成了Web数据通信的基础&#xff0c;并定义了客户端和服务器之间如何请求和传递网页信息。当您在浏览器中输入一个网址时…

STC89C52系列单片机简介

STC89C52系列单片机是由中国宏晶科技&#xff08;STC&#xff09;推出的一款新一代增强型8051内核单片机。它不仅继承了传统8051指令系统的兼容性&#xff0c;还在性能、功耗、抗干扰能力以及性价比方面进行了全面提升&#xff0c;广泛应用于各类嵌入式控制场景&#xff0c;如工…

基于 Docker 环境的 JupyterHub 详细部署手册

本文详细介绍基于Docker Compose的单机版JupyterHub部署方案&#xff0c;通过容器化技术实现多用户Notebook环境的快速搭建。方案采用官方JupyterHub镜像&#xff0c;配置11个端口映射&#xff08;18000-18010&#xff09;支持用户并发&#xff0c;通过数据卷挂载&#xff08;.…

常见的万能密码

目录 1. 通用SQL注入 2. 登录绕过 3. 密码重置 1. 通用SQL注入 or 11-- " or 11-- or aa " or "a""a or 11# " or 11# or 11/* " or 11/* or 11 " or "1""1 2. 登录绕过 admin-- admin or 11-- admin or aa …

04训练windows电脑低算力显卡如何部署pytorch实现GPU加速

大多数人用的电脑的显卡配置可能没有那么强,也就是说,我们很难享受到最新版本pytorch给我们带来的模型训练的速度和效率,为此,我们需要想办法在现有显卡情况下部署应用pytorch。 笔者有一台电脑,显卡算力很低,那么以该电脑为例,为大家介绍如何部署应用pytorch功能。 1…

PPT科研画图插件

PPT科研画图插件 iSlide- 让PPT设计简单起来 | PPT模板下载平台iSlide - 简单&#xff0c;好用的PPT插件&#xff01;拥有30万 原创可商用PPT模板&#xff0c;PPT主题素材&#xff0c;PPT案例&#xff0c;PPT图表&#xff0c;PPT图示&#xff0c;PPT图标&#xff0c;PPT插图和8…

CSS实现背景图片渐变透明

复合写法background: linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, #FFF 82.5%),url(https://example.com/image.jpg) center / cover no-repeat;参数说明&#xff1a;linear-gradient(180deg, rgba(255, 255, 255, 0) 0%, #FFF 82.5%)创建从下至上的垂直渐变&#xff…

基于pyside6的通用机器人遥控控制界面

1. 前言 这两天需要帮一个朋友做一个简单的遥控控制界面&#xff0c;用于控制一台复合机器人(万向轮底盘机械臂旋转云台)&#xff0c;在这里分享一下 2. 开发框架 由于朋友那边的控制接口都是使用python来写的&#xff0c;所以我这里也使用py来完成这个遥控界面的开发。但其…

【iOS】ZARA仿写

【iOS】ZARA仿写 文章目录【iOS】ZARA仿写前言首页发现我的对姓名的更改总结前言 暑假第一个的任务仿写ZARA 虽然不是特别难却有很多小细节需要注意 首页 点进程序出现的就是整个项目最主要的一个点&#xff0c;即首页的无限轮播图&#xff0c;不管是自动轮播还是手动滑动&a…

Kubernetes Pod 调度基础

一、Replication Controller 和 ReplicaSet1、Replication ControllerReplication Controller&#xff08;复制控制器&#xff0c;RC&#xff09;RC 用来确保 Pod 副本数达到期望值&#xff0c;这样可以确保一个或多个同类 Pod 总是可用的。如果存在的 Pod 数量大于设定的值&am…

菜鸟的C#学习(二)

文章目录一、类的访问1、普通类继承抽象类2、普通类继承抽象类&#xff0c;抽象类继承接口&#xff0c;三者联系二、类中方法的访问2.1 抽象方法和虚方法2.2 虚方法和普通方法**1. 调用机制****2. 方法重写****3. 设计意图****4. 性能差异****5. 语法对比表****总结&#xff1a…

04 51单片机之数码管显示

文章目录1、前言2、数码管3、单个数码管引脚定义3-1、单个共阴极3-2、单个共阳极3-3、单个数码管引脚定义4、四位一体数码管引脚定义4-1、四位一体共阴极数码管4-2、四位一体共阳极数码管4-3、四位一体数码管引脚定义5、数码管原理图6、C51数组&#xff08;补充知识点&#xff…

【LLM】OpenRouter调用Anthropic Claude上下文缓存处理

背景 在使用OpenRouter调用Anthropic Claude大模型时&#xff0c;部分模型支持上下文缓存功能。当缓存命中时&#xff0c;调用成本会显著降低。虽然像DeepSeek这类模型自带上下文缓存机制&#xff0c;但本文主要针对构建Agent场景下&#xff0c;需要多次调用Anthropic Claude时…

【C++】第十七节—二叉搜索树(概念+性能分析+增删查+实现+使用场景)

好久不见&#xff0c;我是云边有个稻草人 《C》本文所属专栏—持续更新中—欢迎订阅 目录 一、二叉搜索树的概念 二、二叉搜索树的性能分析 三、二叉搜索树的插入 SearchBinaryTree.h test.cpp 四、⼆叉搜索树的查找 【只有一个3】 【有多个3】 五、⼆叉搜索树的删除…

Redis都有哪些数据结构,使用场景与原理解析

✅ String&#xff1a;字符串&#xff08;最常用、最简单的类型&#xff09;&#x1f4cc; 应用场景&#xff1a;计数器&#xff08;如&#xff1a;页面浏览量、点赞数、转发数等&#xff09;缓存单个值&#xff08;如&#xff1a;token、验证码、用户昵称&#xff09;分布式锁…

将EXCEL或者CSV转换为键值对形式的Markdown文件

# 创建命令行参数解析器parser argparse.ArgumentParser(description将 CSV 或 Excel 文件转换为带标头的 Markdown 格式)# 必需参数parser.add_argument(input_file, help输入文件路径 (CSV 或 Excel))parser.add_argument(output_file, help输出 Markdown 文件路径)# 可选参…

MySQL 配置性能优化实操指南:分版本5.7和8.0适配方案

在 MySQL 性能优化中&#xff0c;不同版本的特性差异会直接影响优化效果。本文基于 MySQL 5.7 和 8.0 两个主流版本&#xff0c;通过版本适配的配置代码、场景举例和通俗解释&#xff0c;让优化方案更精准落地。一、硬件与系统配置优化&#xff08;基础层优化&#xff09;1. 服…