第五章:Rxjava 场景实战技巧

74 阅读4分钟

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;
});

本章总结

  1. 内存泄漏防护

    • 使用 CompositeDisposable 管理订阅
    • 在生命周期结束时调用 dispose()
    • 推荐使用 AutoDispose 库自动绑定生命周期
  2. RxBinding 使用

    • 将 View 事件转换为 Observable
    • 使用 debounce 防止快速重复点击
    • 内置线程安全机制
  3. Retrofit 集成

    • 使用 RxJava3CallAdapterFactory
    • 自动取消网络请求
    • 错误处理符合 RxJava 规范
  4. 性能优化

    • 避免过度订阅(使用 switchMapshare
    • 合理选择调度器(io/computation)
    • 分批处理大数据集
  5. 调试工具

    • RxJavaPlugins 全局监控

Android 最佳实践:在 Android 中使用 RxJava 时,始终将生命周期管理放在首位。使用 CompositeDisposable 或 AutoDispose 管理订阅,结合 RxBinding 简化 UI 事件处理,并通过合理的线程调度优化性能。

在下一章中,我们将深入探讨 RxJava 的性能优化与调试技巧,包括操作符性能对比、Hook 机制使用和内存泄漏检测的高级方法