文章目录
- 一、基础原理与核心概念
- 1. 请简述 RxJava 的观察者模式(Observer Pattern)实现原理,与传统的观察者模式有何区别?
- 2. Observable / Observer / Subscribe / Disposable 关系RxJava 中的 Observable、Observer、Subscribe、Disposable 分别是什么角色?它们之间的关系是怎样的?
- 3. 什么是背压(Backpressure)?在 RxJava 中哪些场景会产生背压?如何解决?
- 4. RxJava 中 Observable 和 Flowable 的本质区别是什么?分别适用于什么场景?
- 5. 解释 RxJava 的事件发射与接收流程(从 subscribe 到 onComplete/onError)
- 二、线程调度 Scheduler
- 1. RxJava 中常用的 Scheduler 有哪些(如 Schedulers.io ()、AndroidSchedulers.mainThread ())?它们的底层实现原理和适用场景是什么?
- 2. subscribeOn() 和 observeOn() 的区别是什么?多次调用它们时,效果如何?**
- 三、设计模式与操作符
- 1. RxJava 用到的设计模式 **
- 2. 请列举常用的创建型操作符(Create、Just、From、Interval、Defer),并说明它们各自的应用场景和区别。
- 3. map 和 flatMap 的区别是什么?在实际开发中如何选择?请结合具体业务场景说明。
- 4. concat、merge、zip 这三个组合操作符的执行逻辑有何不同?分别适用于什么业务场景?
- 5. filter、distinct、skip、take 这类过滤操作符通常用于解决什么问题?
- 6. 什么是 compose 操作符?它与 lift 的区别是什么?如何利用 compose 实现 RxJava 的通用封装(如统一线程调度、错误处理)?
- 7. 如何使用 RxJava 实现网络请求的重试机制(Retry)?请描述实现思路。
- 8. 如何使用 RxJava 实现 防抖(Debounce 功能)?常见的应用场景有哪些(如搜索框联想)?
- 四、内存泄漏与异常
- 1. RxJava 导致内存泄漏的根本原因是什么?(请从引用关系、生命周期角度分析)
- 2.Android Activity/Fragment 中使用 RxJava,如何避免内存泄漏?列举至少 3 种解决方案。
- 3. CompositeDisposable 的作用是什么?为什么要在页面销毁时调用 clear() 或 dispose()?
- 4. RxJava 中的异常处理机制是怎样的?onError 事件一旦触发,整个流会怎样?
- 5. doOnError、onErrorReturn、onErrorResumeNext 的区别是什么?分别适用于什么异常处理场景?
- 6. 如何处理 RxJava 中的未捕获异常
- 五、高级特性与原理
- 1. 分析 RxJava 中 冷 Observable(Cold Observable)和热 Observable(Hot Observable)的区别。ConnectableObservable 如何实现热发射?
- 2. Subject 在 RxJava 中扮演什么角色?有哪些类型(如 PublishSubject、BehaviorSubject)?它们的应用场景是什么?
- 3. RxJava 的背压策略(BackpressureStrategy)有哪几种(BUFFER、DROP、LATEST)?各自的处理逻辑是什么?
- 4. 简述 RxJava 中 变换操作符(如 flatMap)的底层实现原理(涉及 lift 与 Operator)
- 5. 在复杂的链式调用中,如何定位 RxJava 的线程调度问题或内存泄漏问题?
一、基础原理与核心概念
1. 请简述 RxJava 的观察者模式(Observer Pattern)实现原理,与传统的观察者模式有何区别?
RxJava 基于观察者模式扩展,核心是 Observable(被观察者)与 Observer(观察者)通过订阅建立关系。
区别:
- RxJava 是事件流,支持链式调用、线程切换、异常捕获、背压、操作符组合。
- 传统观察者是一对多同步通知,无统一异常处理、无线程调度、无流控制。
- RxJava支持取消订阅(Disposable),传统观察者一般需要手动维护集合注销。
2. Observable / Observer / Subscribe / Disposable 关系RxJava 中的 Observable、Observer、Subscribe、Disposable 分别是什么角色?它们之间的关系是怎样的?
- Observable:事件生产者,决定什么时候发射事件。
- Observer:事件消费者,接收 onNext /onError/onComplete。
- subscribe:订阅方法,建立两者关联,开始发射事件。
- Disposable:订阅关系的管理接口,用于切断订阅、防止内存泄漏。
3. 什么是背压(Backpressure)?在 RxJava 中哪些场景会产生背压?如何解决?
背压:生产者发射速度远大于消费者处理速度,导致事件堆积 OOM。
场景:高频事件(传感器、流数据、大量网络回调)。
解决:
- RxJava 1.x 通过 Backpressure 模式来解决这个问题,而 RxJava 2.x 及之后版本(如 RxJava2/3)则使用 Flowable 来专门处理背压问题。
- 使用 Flowable 配合 BackpressureStrategy。
- 使用操作符限流:sample、throttle、debounce、onBackpressureBuffer 等。
- 降低发射频率或提升消费效率。
4. RxJava 中 Observable 和 Flowable 的本质区别是什么?分别适用于什么场景?
答案:
Observable:不支持背压,适合事件量可控场景。
Flowable:支持背压,内置缓冲区与丢弃策略,适合高频率、大流量数据流。
Flowable 下游通过request(n)告诉上游「我能消费多少条」,上游不会盲目发射
importio.reactivex.rxjava3.core.Flowable;importio.reactivex.rxjava3.core.FlowableSubscriber;importio.reactivex.rxjava3.schedulers.Schedulers;importorg.reactivestreams.Subscription;publicclassFlowableBackPressureDemo{publicstatic voidmain(String[]args)throws InterruptedException{// 上游:快速发射1~100,速度很快Flowable.range(1,100).subscribeOn(Schedulers.io()).subscribe(new FlowableSubscriber<Integer>(){privateSubscription subscription;// 初始请求 5 个事件privatefinalint batch=5;@OverridepublicvoidonSubscribe(Subscription s){System.out.println("==== onSubscribe,开始订阅");subscription=s;// 告诉上游:我准备好消费 5 条subscription.request(batch);}@OverridepublicvoidonNext(Integer integer){System.out.println("消费:"+integer+",线程:"+Thread.currentThread().getName());// 模拟消费慢,每消费一条休眠300mstry{Thread.sleep(300);}catch(InterruptedException e){e.printStackTrace();}// 每消费完一批,再继续拉取下一批if(integer%batch==0){System.out.println("==== 一批消费完毕,再请求"+batch+"条");subscription.request(batch);}}@OverridepublicvoidonError(Throwable t){t.printStackTrace();}@OverridepublicvoidonComplete(){System.out.println("==== 全部完成");}});Thread.sleep(10000);}}演示 4 种常用背压策略
Flowable.range(1,1000).onBackpressureBuffer()// 缓存来不及处理的数据.subscribeOn(Schedulers.io()).observeOn(Schedulers.single()).subscribe(v->{Thread.sleep(100);System.out.println("buffer消费:"+v);},Throwable::printStackTrace);Thread.sleep(5000);5. 解释 RxJava 的事件发射与接收流程(从 subscribe 到 onComplete/onError)
- subscribe () → 订阅建立 → Observable 发射 onNext → 正常结束 onComplete / 异常
onError → 事件终止。一旦 onError /onComplete,流终止,不再接收事件。
二、线程调度 Scheduler
1. RxJava 中常用的 Scheduler 有哪些(如 Schedulers.io ()、AndroidSchedulers.mainThread ())?它们的底层实现原理和适用场景是什么?
- Schedulers.io ():IO 线程池,无上限,适合网络、数据库、文件。
- Schedulers.computation ():CPU 密集型,线程数 = CPU 核心数。
- AndroidSchedulers.mainThread ():Android 主线程,用于更新 UI。
- Schedulers.newThread ():每次新建线程,不推荐频繁使用。
2. subscribeOn() 和 observeOn() 的区别是什么?多次调用它们时,效果如何?**
- subscribeOn:指定被观察者执行线程,多次调用只有第一次生效。
- observeOn:指定观察者执行线程,每次调用都生效,可多次切换。
三、设计模式与操作符
1. RxJava 用到的设计模式 **
- 观察者模式:核心订阅分发。
- 装饰器模式:几乎所有操作符(map/flatMap)都是对上游 Observable 包装。
- 适配器模式:将各种异步逻辑适配成统一 Observable 流。
- 迭代器模式:内部事件序列遍历。
- 策略模式:Scheduler、BackpressureStrategy。
2. 请列举常用的创建型操作符(Create、Just、From、Interval、Defer),并说明它们各自的应用场景和区别。
- just:快速发射固定数据。
- fromArray/fromIterable:遍历集合。
- create:自定义发射逻辑(需手动处理 Disposable)。
- interval:定时循环,常用于轮询。
- defer:延迟创建 Observable,每次订阅都生成新实例,确保获取最新数据。
3. map 和 flatMap 的区别是什么?在实际开发中如何选择?请结合具体业务场景说明。
map:数据类型转换,1:1 返回普通对象。
flatMap:1:N 转换,返回Observable,可并发、无序。
适用:map 做简单转换;flatMap 做嵌套请求、多源合并。
4. concat、merge、zip 这三个组合操作符的执行逻辑有何不同?分别适用于什么业务场景?
- concat:串行执行,按顺序,必须等上一个完成。
- merge:并行执行,谁先完成谁先回调,无序。
- zip:按个数配对,必须等所有源都发射对应事件才回调。
5. filter、distinct、skip、take 这类过滤操作符通常用于解决什么问题?
- filter:条件过滤。
- distinct:去重。
- take:取前 N 个。
- skip:跳过前 N 个。
常用于列表筛选、重复请求拦截、节流。
6. 什么是 compose 操作符?它与 lift 的区别是什么?如何利用 compose 实现 RxJava 的通用封装(如统一线程调度、错误处理)?
- lift:底层操作符,针对事件流做转换,面向 Observer 包装。
- compose:面向 Observable 整体封装,适合统一线程调度、统一错误处理,可复用。
7. 如何使用 RxJava 实现网络请求的重试机制(Retry)?请描述实现思路。
使用 retry /retryWhen,配合条件判断(网络码、异常类型、次数限制、延迟策略)。
8. 如何使用 RxJava 实现 防抖(Debounce 功能)?常见的应用场景有哪些(如搜索框联想)?
搜索框联想、按钮防重复点击、快速滑动停止后加载。
四、内存泄漏与异常
1. RxJava 导致内存泄漏的根本原因是什么?(请从引用关系、生命周期角度分析)
- Observable 持有 Activity/Fragment 引用(匿名内部类)。
- 异步任务未完成,页面已销毁,Observer 无法被回收。
- 未取消订阅,Disposable 未 dispose。
2.Android Activity/Fragment 中使用 RxJava,如何避免内存泄漏?列举至少 3 种解决方案。
- 使用CompositeDisposable统一管理,onDestroy 调用 clear/dispose。
- 使用RxLifecycle绑定生命周期自动解绑。
- 避免匿名内部类持有 Activity 引用,使用静态内部类 + 弱引用。
- 合理使用 takeUntil、autoDispose。
3. CompositeDisposable 的作用是什么?为什么要在页面销毁时调用 clear() 或 dispose()?
管理多个 Disposable,页面销毁时统一切断所有订阅,防止泄漏。
4. RxJava 中的异常处理机制是怎样的?onError 事件一旦触发,整个流会怎样?
异常会向上游传递,触发onError 后整个流终止,无法继续接收事件。
5. doOnError、onErrorReturn、onErrorResumeNext 的区别是什么?分别适用于什么异常处理场景?
doOnError:副作用,监听异常,不恢复流。
onErrorReturn:捕获异常,发射一个默认值,然后正常结束。
onErrorResumeNext:异常后切换到新 Observable 继续发射。
6. 如何处理 RxJava 中的未捕获异常
- 必须实现 Observer 的 onError;
- 全局可通过 RxJavaPlugins.setErrorHandler 兜底。
五、高级特性与原理
1. 分析 RxJava 中 冷 Observable(Cold Observable)和热 Observable(Hot Observable)的区别。ConnectableObservable 如何实现热发射?
- 冷:订阅才开始发射,每个订阅者独立事件流(如网络请求)。
- 热:不依赖订阅,提前发射,多订阅者共享事件(如 Subject、事件总线)。
- ConnectableObservable 可将冷流转热,publish + connect。
2. Subject 在 RxJava 中扮演什么角色?有哪些类型(如 PublishSubject、BehaviorSubject)?它们的应用场景是什么?
- PublishSubject:只接收订阅后事件。
- BehaviorSubject:接收订阅前最后一个 + 后续事件。
- ReplaySubject:重放历史所有事件。
- AsyncSubject:只接收最后一个事件并在完成时发射。
- 常用于事件总线、跨页面通信。
3. RxJava 的背压策略(BackpressureStrategy)有哪几种(BUFFER、DROP、LATEST)?各自的处理逻辑是什么?
- BUFFER:缓存队列,可能 OOM。
- DROP:丢弃新事件。
- LATEST:保留最新,覆盖旧的。
- ERROR:直接抛 MissingBackpressureException。
4. 简述 RxJava 中 变换操作符(如 flatMap)的底层实现原理(涉及 lift 与 Operator)
通过 lift 包装上游 Observable,内部创建新 Observable,将每个事件转为新 Observable 并合并发射。
5. 在复杂的链式调用中,如何定位 RxJava 的线程调度问题或内存泄漏问题?
- 线程问题:查看 subscribeOn/observeOn 位置。
- 泄漏:LeakCanary + 检查 Disposable 是否释放。
- 异常:打印完整堆栈,检查是否遗漏 onError。