1. 内存泄漏防护
1.1 内存泄漏原因分析
在 Android 中使用 RxJava 最常见的问题是生命周期不匹配导致的内存泄漏:
// 危险示例:在 Activity 中使用
Observable.interval(1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(time -> {
// 更新 UI
textView.setText("Time: " + time);
});
问题分析:
- 当 Activity 被销毁时,interval 仍在运行
- 订阅持有 Activity 的隐式引用(textView)
- 导致 Activity 无法被垃圾回收
1.2 CompositeDisposable 解决方案
public class MainActivity extends AppCompatActivity {
private CompositeDisposable disposables = new CompositeDisposable();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Disposable timerDisposable = Observable.interval(1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(time -> {
textView.setText("Time: " + time);
});
disposables.add(timerDisposable);
}
@Override
protected void onDestroy() {
super.onDestroy();
disposables.dispose(); // 取消所有订阅
}
}
CompositeDisposable 源码解析:
public final class CompositeDisposable implements Disposable, DisposableContainer {
private final LinkedHashSet<Disposable> resources;
private volatile boolean disposed;
public void dispose() {
if (disposed) return;
LinkedHashSet<Disposable> set;
synchronized (this) {
if (disposed) return;
disposed = true;
set = resources;
resources = null;
}
dispose(set);
}
private void dispose(Collection<Disposable> disposables) {
if (disposables == null) return;
List<Throwable> errors = null;
for (Disposable d : disposables) {
try {
d.dispose();
} catch (Throwable ex) {
// 收集错误但不中断
if (errors == null) errors = new ArrayList<>();
errors.add(ex);
}
}
if (errors != null) {
if (errors.size() == 1) {
throw ExceptionHelper.wrapOrThrow(errors.get(0));
}
throw new CompositeException(errors);
}
}
}
1.3 AutoDispose 库的使用
对于更复杂的场景,可以使用 AutoDispose 库:
dependencies {
implementation 'com.uber.autodispose2:autodispose:2.1.1'
implementation 'com.uber.autodispose2:autodispose-android:2.1.1'
implementation 'com.uber.autodispose2:autodispose-lifecycle:2.1.1'
}
Observable.interval(1, TimeUnit.SECONDS)
.observeOn(AndroidSchedulers.mainThread())
.as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this)))
.subscribe(time -> {
textView.setText("Time: " + time);
});
原理分析:
- 使用
AndroidLifecycleScopeProvider绑定生命周期 - 在
onDestroy时自动取消订阅 - 支持各种生命周期事件(onPause、onStop 等)
2. RxBinding 原理与应用
2.1 View 事件转 Observable
RxView.clicks() 源码解析:
public static Observable<Object> clicks(View view) {
return new ViewClickObservable(view);
}
static final class ViewClickObservable extends Observable<Object> {
private final View view;
ViewClickObservable(View view) {
this.view = view;
}
@Override
protected void subscribeActual(Observer<? super Object> observer) {
Listener listener = new Listener(view, observer);
observer.onSubscribe(listener);
view.setOnClickListener(listener);
}
static final class Listener extends MainThreadDisposable implements OnClickListener {
private final View view;
private final Observer<? super Object> observer;
Listener(View view, Observer<? super Object> observer) {
this.view = view;
this.observer = observer;
}
@Override
public void onClick(View v) {
if (!isDisposed()) {
observer.onNext(Notification.INSTANCE);
}
}
@Override
protected void onDispose() {
view.setOnClickListener(null);
}
}
}
关键设计:
- 使用
MainThreadDisposable确保在主线程操作 - 在
onDispose中解除监听,避免内存泄漏 - 使用
Notification.INSTANCE作为占位符事件
2.2 防抖处理 (debounce)
RxView.clicks(button)
.debounce(500, TimeUnit.MILLISECONDS) // 500ms 防抖
.observeOn(AndroidSchedulers.mainThread())
.subscribe(v -> {
// 处理点击事件
});
debounce 原理:
- 在指定时间窗口内只接收最后一次事件
- 有效防止快速重复点击
3. Retrofit + RxJava 集成
3.1 基本集成方式
public interface ApiService {
@GET("users/{user}")
Observable<User> getUser(@Path("user") String username);
}
// 创建 Retrofit 实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
// 使用
ApiService api = retrofit.create(ApiService.class);
api.getUser("octocat")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(user -> {
// 显示用户信息
}, error -> {
// 处理错误
});
3.2 CallEnqueueObservable 源码解析
final class CallEnqueueObservable<T> extends Observable<T> {
private final Call<T> originalCall;
@Override
protected void subscribeActual(Observer<? super T> observer) {
Call<T> call = originalCall.clone();
CallCallback<T> callback = new CallCallback<>(call, observer);
observer.onSubscribe(callback);
if (!callback.isDisposed()) {
call.enqueue(callback);
}
}
private static final class CallCallback<T> implements Disposable, Callback<T> {
private final Call<?> call;
private final Observer<? super T> observer;
private volatile boolean disposed;
CallCallback(Call<?> call, Observer<? super T> observer) {
this.call = call;
this.observer = observer;
}
@Override
public void onResponse(Call<T> call, Response<T> response) {
if (disposed) return;
try {
if (response.isSuccessful()) {
observer.onNext(response.body());
observer.onComplete();
} else {
observer.onError(new HttpException(response));
}
} catch (Throwable t) {
Exceptions.throwIfFatal(t);
observer.onError(t);
}
}
@Override
public void onFailure(Call<T> call, Throwable t) {
if (disposed) return;
try {
observer.onError(t);
} finally {
dispose();
}
}
@Override
public void dispose() {
disposed = true;
call.cancel();
}
}
}
关键点:
- 使用
call.clone()确保每次订阅都是新请求 - 在
dispose()中调用call.cancel()取消网络请求 - 错误处理符合 RxJava 规范
4. LiveData 与 RxJava 互操作
4.1 LiveDataReactiveStreams 使用
// 将 Observable 转换为 LiveData
Observable<User> userObservable = api.getUser("octocat")
.subscribeOn(Schedulers.io());
LiveData<User> userLiveData = LiveDataReactiveStreams.fromPublisher(
userObservable.toFlowable(BackpressureStrategy.LATEST)
);
// 在 ViewModel 中使用
public class UserViewModel extends ViewModel {
private final MutableLiveData<User> user = new MutableLiveData<>();
public void loadUser(String username) {
api.getUser(username)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(user::setValue, error -> {
// 处理错误
});
}
public LiveData<User> getUser() {
return user;
}
}
4.2 RxLifecycle 替代方案
// 在 Fragment 中使用
api.getUser("octocat")
.compose(bindToLifecycle()) // 自动绑定生命周期
.subscribe(user -> {
// 更新 UI
});
实现原理:
- 使用
takeUntil操作符监听生命周期事件 - 当生命周期达到指定状态时自动取消订阅
5. 性能优化策略
5.1 避免过度订阅
问题场景:
// 每次点击都发起新请求
RxView.clicks(button)
.flatMap(e -> api.getData()) // 每次点击创建新订阅
.subscribe();
优化方案:
// 使用 switchMap 取消前一个请求
RxView.clicks(button)
.switchMap(e -> api.getData()) // 取消前一个未完成请求
.subscribe();
// 或者使用 replay + autoConnect 共享订阅
Observable<Data> shared = api.getData()
.subscribeOn(Schedulers.io())
.replay(1)
.autoConnect();
RxView.clicks(button)
.flatMap(e -> shared) // 共享同一个订阅
.subscribe();
5.2 后台任务优化
// 使用 Schedulers.io() 的优化
Observable.fromCallable(() -> {
return heavyOperation(); // 耗时操作
})
.subscribeOn(Schedulers.io()) // 使用 I/O 线程池
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
线程选择指南:
| 操作类型 | 推荐调度器 | 说明 |
|---|---|---|
| 网络请求 | Schedulers.io() | 适合 I/O 密集型任务 |
| 计算任务 | Schedulers.computation() | 适合 CPU 密集型任务 |
| 数据库操作 | 自定义有界线程池 | 避免线程过多竞争 |
| UI 更新 | AndroidSchedulers.mainThread() | 必须在主线程 |
5.3 内存占用优化
// 大型列表处理优化
Observable.fromIterable(hugeList)
.buffer(100) // 分批处理,每批100项
.flatMap(batch ->
processBatch(batch).subscribeOn(Schedulers.computation())
)
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
优化技巧:
- 使用
buffer分批处理大型数据集 - 避免在内存中保留完整结果集
- 使用
Flowable处理大数据流
6. 调试与监控
6.1 RxJava 调试工具
// 启用 RxJava 调试模式
RxJavaPlugins.setErrorHandler(e -> {
Log.e("RxJavaError", "全局错误", e);
});
// 跟踪订阅链
RxJavaPlugins.setOnObservableAssembly(observable -> {
Log.d("RxJavaDebug", "创建: " + observable);
return observable;
});
本章总结
-
内存泄漏防护:
- 使用
CompositeDisposable管理订阅 - 在生命周期结束时调用
dispose() - 推荐使用
AutoDispose库自动绑定生命周期
- 使用
-
RxBinding 使用:
- 将 View 事件转换为 Observable
- 使用
debounce防止快速重复点击 - 内置线程安全机制
-
Retrofit 集成:
- 使用
RxJava3CallAdapterFactory - 自动取消网络请求
- 错误处理符合 RxJava 规范
- 使用
-
性能优化:
- 避免过度订阅(使用
switchMap、share) - 合理选择调度器(io/computation)
- 分批处理大数据集
- 避免过度订阅(使用
-
调试工具:
- RxJavaPlugins 全局监控
Android 最佳实践:在 Android 中使用 RxJava 时,始终将生命周期管理放在首位。使用
CompositeDisposable或AutoDispose管理订阅,结合RxBinding简化 UI 事件处理,并通过合理的线程调度优化性能。
在下一章中,我们将深入探讨 RxJava 的性能优化与调试技巧,包括操作符性能对比、Hook 机制使用和内存泄漏检测的高级方法