一、RxJava线程切换核心原理
1. 线程调度器(Scheduler)体系
RxJava通过Scheduler
抽象类实现线程调度,主要实现类包括:
// 常用调度器
Schedulers.io() // IO密集型任务
Schedulers.computation() // CPU密集型计算
Schedulers.newThread() // 每次创建新线程
Schedulers.single() // 单一线程池
AndroidSchedulers.mainThread() // Android主线程
2. 线程切换实现机制
关键源码分析:
// Observable.subscribeOn()原理
public final Observable<T> subscribeOn(Scheduler scheduler) {
return new ObservableSubscribeOn<>(this, scheduler);
}
static final class ObservableSubscribeOn<T> extends AbstractObservableWithUpstream<T,T> {
final Scheduler scheduler;
public void subscribeActual(Observer<? super T> observer) {
// 创建Worker负责实际调度
Scheduler.Worker w = scheduler.createWorker();
// 使用Worker调度订阅过程
w.schedule(new SubscribeTask(new SubscribeOnObserver<>(observer)));
}
}
工作流程:
-
通过
subscribeOn
指定上游执行线程 -
通过
observeOn
指定下游执行线程 -
每个操作符都会创建新的
Observer
包装前一个 -
事件从下游向上游传递订阅关系,从上游向下游传递数据
3. 线程切换示例
Observable.create(emitter -> {
// 在IO线程执行(由subscribeOn指定)
emitter.onNext(doNetworkRequest());
})
.map(data -> {
// 仍在IO线程(未切换)
return processData(data);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
// 在主线程处理结果(由observeOn指定)
updateUI(result);
});
二、RxJava1与RxJava2核心区别
1. 架构设计差异
特性 | RxJava1 | RxJava2 |
---|---|---|
基础接口 | Subscriber /Observer | Observer /FlowableSubscriber |
背压支持 | 部分支持(通过onBackpressureXXX ) | 明确分离(Flowable 支持背压,Observable 不支持) |
Null值处理 | 允许发射null | 禁止null值(抛出NullPointerException) |
订阅关系 | Subscription | Disposable |
2. 背压处理对比
RxJava1背压问题:
Observable.range(1, 1_000_000)
.subscribe(new Subscriber<Integer>() {
@Override
public void onStart() {
request(1); // 需要手动控制请求数量
}
@Override
public void onNext(Integer i) {
// 处理数据
request(1); // 处理完再请求下一个
}
});
RxJava2背压改进:
Flowable.range(1, 1_000_000)
.onBackpressureBuffer(100) // 明确的背压策略
.subscribe(new Subscriber<Integer>() {
Subscription s;
@Override
public void onSubscribe(Subscription s) {
this.s = s;
s.request(1); // 初始请求量
}
@Override
public void onNext(Integer i) {
// 处理数据
s.request(1); // 继续请求
}
});
RxJava2改进点:
-
对象池优化:减少对象创建(如
Notification
对象池化) -
更轻量的订阅:
Disposable
比Subscription
更简单 -
操作符重写:70%的操作符被重新实现优化
// RxJava2的对象池使用示例 public final class NotificationLite { private static final Object[] EMPTY = new Object[0]; private static final AtomicReference<Object[]> pool = new AtomicReference<>(EMPTY); static Object[] get() { Object[] o = pool.get(); if (o != EMPTY && pool.compareAndSet(o, EMPTY)) { return o; } return new Object[2]; } }
三、深度问题解析
Q1:RxJava如何保证观察者在主线程更新UI?
回答要点:
-
AndroidSchedulers.mainThread()
本质是HandlerScheduler
-
通过
Looper.getMainLooper()
创建主线程Handler -
observeOn
切换时使用Handler.post()
传递事件
关键代码:
// HandlerScheduler实现原理
public final class HandlerScheduler extends Scheduler {
private final Handler handler;
public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
// 使用Handler切换到主线程
ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);
handler.postDelayed(scheduled, unit.toMillis(delay));
return scheduled;
}
}
Q2:RxJava1升级到RxJava2需要注意什么?
迁移要点:
-
接口变更:
// RxJava1 Subscription subscription = observable.subscribe(); // RxJava2 Disposable disposable = observable.subscribe();
-
Null值处理:
// RxJava1允许 Observable.just(null); // RxJava2需要包装 Observable.just(Optional.ofNullable(null));
-
背压选择:
// 根据需求选择Flowable或Observable Flowable.create(emitter -> { // 支持背压 }, BackpressureStrategy.BUFFER);
Q3:RxJava线程切换会产生内存泄漏吗?如何解决?
风险与解决方案:
-
泄漏风险:
-
长时间运行的操作持有Activity引用
-
未及时取消订阅
-
-
解决方案:
// 使用CompositeDisposable管理 private CompositeDisposable disposables = new CompositeDisposable(); void startRequest() { disposables.add(observable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::updateUI)); } @Override protected void onDestroy() { disposables.clear(); // 取消所有订阅 super.onDestroy(); }
四、版本差异对比表格
对比维度 | RxJava1 | RxJava2 |
---|---|---|
基础观察者 | Observer | Observer + FlowableSubscriber |
背压支持 | 部分操作符支持 | 明确Flowable 类型 |
Null处理 | 允许 | 禁止 |
订阅控制 | Subscription.unsubscribe() | Disposable.dispose() |
操作符数量 | 约130个 | 约180个 |
性能 | 一般 | 提升30%-50% |
内存占用 | 较高 | 优化对象池 |
五、实战建议
-
版本选择:
-
新项目直接使用RxJava3(基于RxJava2改进)
-
老项目逐步迁移到RxJava2/3
-
-
线程使用规范:
// 推荐写法 disposable = observable .subscribeOn(Schedulers.io()) // 网络/磁盘IO .observeOn(Schedulers.computation()) // 数据转换 .observeOn(AndroidSchedulers.mainThread()) // UI更新 .subscribe();
-
错误处理改进:
// RxJava2更严格的错误处理 Observable.create(emitter -> { try { emitter.onNext(doSomething()); emitter.onComplete(); } catch (Exception e) { emitter.onError(e); // 必须调用onError } });
理解RxJava线程切换机制和版本差异,有助于在面试中展示对响应式编程的深入理解,同时在实际开发中做出更合理的技术选型和性能优化。