RxJava 框架详细介绍

65 阅读13分钟

1. 什么是 RxJava?

RxJava 是 Reactive Extensions 在 JVM 上的一个实现,它是一个基于观察者模式、用于构建异步和基于事件的程序的库。其核心思想是:异步数据流编程

你可以将任何东西都看作数据流:变量、用户输入、属性、缓存、网络请求返回的数据等。RxJava 提供了强大的工具(操作符)来对这些数据流进行创建、转换、组合和过滤。

2. 核心概念

RxJava 2.x/3.x 的核心构建块是以下几个接口:

  1. Observable (可观察者) : 数据的生产者,它可以发射0个、1个或多个数据项,也可以发射一个错误或完成信号。它不支持背压。

  2. Observer (观察者) : 数据的消费者,它订阅 Observable 并对其发出的事件做出反应。

    • onSubscribe(Disposable d): 建立订阅关系时调用。
    • onNext(T t): 接收到一个数据项时调用。
    • onError(Throwable e): 当 Observable 发生错误时调用。
    • onComplete(): 当 Observable 完成所有数据发射时调用。
  3. Flowable: 与 Observable 类似,但支持背压。当生产者的发射速度超过消费者的处理速度时,用于处理流量控制。适用于大量数据或网络请求等场景。

  4. Single: 只发射一个数据项或一个错误信号。类似于 Promise。适用于网络请求等单次异步操作。

    • onSuccess(T t)
    • onError(Throwable e)
  5. Completable: 不发射任何数据,只处理完成或错误信号。适用于只知道操作是否成功,而不关心返回值的场景(如:缓存写入)。

  6. Maybe: 可能发射0个或1个数据项,或者一个错误/完成信号。是 Single 和 Completable 的结合。

  7. Disposable: 订阅的凭证,用于取消订阅,防止内存泄漏。

  8. Scheduler (调度器): 用于控制异步操作的线程。


二、原理

1. 观察者模式

RxJava 的核心是观察者模式的扩展。

  • 被观察者 (Observable)  和 观察者 (Observer)  通过 subscribe 方法建立订阅关系。
  • 一旦订阅建立,Observable 就可以向 Observer 推送数据。
  • 这是一种  “推”  的模式,而不是传统的  “拉”  的模式。

2. 事件流

一个 Observable 的生命周期由三种类型的事件组成(遵循约定):

  • onNext: 可以多次调用,每次传递一个数据。
  • onError 或 onComplete: 只能调用其中一个,并且是终止性事件,调用后不能再发送任何事件。

3. 背压 (Backpressure)

问题: 当生产者 (Observable) 发射数据的速度远快于消费者 (Observer) 处理数据的速度时,会导致未处理的数据在内存中堆积,最终导致 OutOfMemoryError

解决方案

  • 使用 Flowable 代替 ObservableFlowable 实现了 响应式流规范,提供了多种背压策略:

    • MISSING: 不处理,由下游通过 onBackpressureXXX 操作符指定。
    • ERROR: 缓存区溢出时立即抛出 MissingBackpressureException
    • BUFFER: 无限制地缓存所有数据(可能导致 OOM)。
    • DROP: 如果缓存区满了,就丢弃新来的数据。
    • LATEST: 只保留最新的数据。

三、操作符 (Operators)

操作符是 RxJava 的魔力所在,它们允许你以声明式的方式对数据流进行复杂的转换和组合。

1. 创建操作符

用于创建 Observable/Flowable

  • just() : 将一个或多个对象转换为发射这些对象的 Observable

    java

    Observable.just("Hello", "World").subscribe(System.out::println);
    
  • fromIterable() : 从一个 Iterable(如 ListSet)创建。

    java

    List<String> list = Arrays.asList("Apple", "Banana", "Orange");
    Observable.fromIterable(list).subscribe(System.out::println);
    
  • create() : 通过手动调用 onNextonErroronComplete 来创建。

    java

    Observable.create(emitter -> {
        emitter.onNext("Data 1");
        emitter.onNext("Data 2");
        emitter.onComplete();
    }).subscribe(System.out::println);
    
  • interval() : 每隔一段时间发射一个递增的 Long 数字。

    java

    // 每隔1秒发射一个数字,从0开始
    Observable.interval(1, TimeUnit.SECONDS)
              .subscribe(num -> System.out.println("Tick: " + num));
    // 注意:需要在主线程中运行,否则程序会立即退出
    
  • range() : 发射一个整数序列。

    java

    Observable.range(1, 5).subscribe(num -> System.out.println("Number: " + num));
    // 输出:1, 2, 3, 4, 5
    

2. 转换操作符

用于对发射的数据进行转换。

  • map() : 一对一转换。

    java

    Observable.just("hello")
              .map(String::toUpperCase)
              .subscribe(result -> System.out.println(result)); // 输出: HELLO
    
  • flatMap() : 一对多转换,将每个数据项转换为一个新的 Observable,然后合并这些 Observable 的发射物。顺序不保证

    java

    Observable.just("user1", "user2")
              .flatMap(userId -> getUserDetailsFromNetwork(userId)) // 返回 Observable<UserDetail>
              .subscribe(userDetail -> System.out.println(userDetail.getName()));
    
  • concatMap() : 与 flatMap 类似,但保证顺序。

  • switchMap() : 当源发射一个新项时,取消并停止镜像之前项生成的 Observable,只监视最新的一个。适用于搜索建议

  • buffer() : 定期将收集到的数据打包成一个集合发射。

    java

    Observable.range(1, 10)
              .buffer(3) // 每3个数字打包成一个List
              .subscribe(list -> System.out.println("Buffer: " + list));
    // 输出: [1,2,3], [4,5,6], [7,8,9], [10]
    

3. 过滤操作符

用于从数据流中选择特定的数据。

  • filter() : 过滤数据。

    java

    Observable.range(1, 10)
              .filter(num -> num % 2 == 0)
              .subscribe(evenNum -> System.out.println("Even: " + evenNum));
    
  • take() : 只取前 n 项。

    java

    Observable.range(1, 10).take(3).subscribe(num -> System.out.println(num)); // 输出: 1,2,3
    
  • distinct() : 去重。

  • debounce() : 仅在过了一段指定的时间后没有发射其他数据项时,才发射一个数据。用于搜索框输入

    java

    // 假设 searchTextView 是一个文本输入框
    RxTextView.textChanges(searchTextView) // 需要 RxBinding 库
              .debounce(300, TimeUnit.MILLISECONDS) // 等待用户停止输入300毫秒
              .subscribe(charSequence -> performSearch(charSequence.toString()));
    

4. 组合操作符

用于组合多个 Observable

  • zip() : 将多个 Observable 的发射物按顺序组合成一个新的发射物。

    java

    Observable<String> names = Observable.just("Alice", "Bob");
    Observable<Integer> ages = Observable.just(25, 30);
    
    Observable.zip(names, ages, (name, age) -> name + " is " + age + " years old")
              .subscribe(System.out::println);
    // 输出: Alice is 25 years old, Bob is 30 years old
    
  • merge() : 将多个 Observable 的发射物合并,就像来自一个 Observable 一样。时间线交错

  • concat() : 按顺序执行多个 Observable,一个接一个。保证顺序

5. 错误处理操作符

  • onErrorReturn() : 遇到错误时,发射一个默认值并正常终止。

    java

    Observable.create(emitter -> { emitter.onError(new RuntimeException("Oops!")); })
              .onErrorReturn(throwable -> "Default Value")
              .subscribe(
                  item -> System.out.println("Item: " + item), // 输出: Item: Default Value
                  error -> System.out.println("Error: " + error) // 不会调用
              );
    
  • onErrorResumeNext() : 遇到错误时,开始发射另一个 Observable 的数据序列。

  • retry() : 当发生错误时,重新订阅(重试)。

    java

    apiService.getData()
              .retry(3) // 最多重试3次
              .subscribeOn(Schedulers.io())
              .observeOn(AndroidSchedulers.mainThread())
              .subscribe(...);
    

四、线程调度 (Schedulers)

RxJava 的强大之处在于它简洁而强大的线程控制。

1. 核心调度器

  • Schedulers.io() : 用于 I/O 密集型工作,如网络请求、文件操作。线程池可根据需要增长。
  • Schedulers.computation() : 用于计算密集型工作,如数据转换、CPU 密集型操作。线程数固定为 CPU 核心数。
  • Schedulers.newThread() : 为每个工作创建一个新线程。不常用,因为效率低。
  • Schedulers.single() : 使用一个单一线程,所有任务按顺序执行。
  • Schedulers.trampoline() : 在当前线程工作,但排队执行。
  • AndroidSchedulers.mainThread()  (RxAndroid): 指定在 Android 主线程(UI线程)运行。

2. 线程控制操作符

  • subscribeOn()指定 Observable 在哪个调度器上执行(即数据生产的线程) 。多次调用只有第一个生效。
  • observeOn()指定 Observer 在哪个调度器上接收数据(即数据消费的线程) 。可以多次调用,改变后续操作的线程。

3. 经典用例:网络请求 + 更新 UI

java

apiService.getUser(userId) // 这是一个返回 Observable<User> 的 Retrofit 接口
        .subscribeOn(Schedulers.io())        // 在 IO 线程执行网络请求
        .observeOn(AndroidSchedulers.mainThread()) // 在主线程处理结果,更新 UI
        .subscribe(new Observer<User>() {
            @Override
            public void onSubscribe(Disposable d) { }

            @Override
            public void onNext(User user) {
                // 在主线程,安全地更新 UI
                textViewName.setText(user.getName());
                imageViewAvatar.setImageUrl(user.getAvatarUrl());
            }

            @Override
            public void onError(Throwable e) {
                // 在主线程,显示错误信息
                Toast.makeText(context, "Error: " + e.getMessage(), Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onComplete() { }
        });

五、实际开发中常用的用法用例

1. 网络请求 (Retrofit + RxJava)

Retrofit 原生支持返回 RxJava 类型。

接口定义

java

public interface ApiService {
    @GET("user/{id}")
    Observable<User> getUser(@Path("id") String userId);

    @GET("users")
    Observable<List<User>> getUsers();

    @POST("user/update")
    Completable updateUser(@Body User user);
}

使用

java

// 组合多个请求
Observable.zip(
    apiService.getUser("1"),
    apiService.getUser("2"),
    (user1, user2) -> {
        // 合并两个用户的数据
        return new CombinedData(user1, user2);
    })
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(combinedData -> updateUI(combinedData));

// 顺序执行请求
apiService.getUser("1")
        .flatMap(user -> apiService.getUserProfile(user.getProfileId()))
        .subscribe(...);

2. 数据库操作 (Room + RxJava)

Room 数据库库也支持返回 RxJava 类型。

java

@Dao
public interface UserDao {
    @Query("SELECT * FROM users")
    Flowable<List<User>> getUsers(); // 返回 Flowable,当数据变化时自动发射新数据

    @Insert
    Completable insertUser(User user);
}

// 在 ViewModel 中
userDao.getUsers()
      .subscribeOn(Schedulers.io())
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe(users -> {
          // 自动更新 UI,当数据库中的用户数据发生变化时
          adapter.setData(users);
      });

3. 响应式 UI 事件 (RxBinding)

使用 RxBinding 库将 UI 事件转换为 Observable。

java

// 按钮点击防抖
RxView.clicks(button)
      .throttleFirst(500, TimeUnit.MILLISECONDS) // 防止连续点击
      .subscribe(aVoid -> performAction());

// 监听多个输入框,直到所有都非空才启用按钮
Observable.combineLatest(
    RxTextView.textChanges(editText1).map(CharSequence::toString),
    RxTextView.textChanges(editText2).map(CharSequence::toString),
    (text1, text2) -> !text1.isEmpty() && !text2.isEmpty()
).subscribe(button::setEnabled);

4. 定时任务 / 轮询

java

// 每隔5秒轮询一次服务器
Observable.interval(0, 5, TimeUnit.SECONDS)
        .flatMap(tick -> apiService.checkForUpdates())
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(update -> handleUpdate(update));

// 延时执行
Observable.timer(2, TimeUnit.SECONDS)
        .subscribe(aLong -> {
            // 2秒后执行
            showSplashScreen();
        });

5. 事件总线 (RxBus)

一个简单的、基于 RxJava 的事件总线模式。

java

public class RxBus {
    private static final RxBus INSTANCE = new RxBus();
    private final PublishSubject<Object> bus = PublishSubject.create();

    public static RxBus getInstance() { return INSTANCE; }

    public void send(Object event) {
        bus.onNext(event);
    }

    public <T> Observable<T> toObservable(Class<T> eventType) {
        return bus.ofType(eventType); // 只接收指定类型的事件
    }
}

// 发送事件
RxBus.getInstance().send(new UserLoggedInEvent(user));

// 接收事件
RxBus.getInstance().toObservable(UserLoggedInEvent.class)
        .subscribe(event -> {
            // 处理用户登录事件
            updateUserInfo(event.getUser());
        });

六、总结与最佳实践

  1. 内存泄漏: 在 Android 中,记得在 onDestroy 或类似生命周期方法中调用 Disposable.dispose() 来取消订阅。可以使用 CompositeDisposable 来管理多个订阅。

    java

    private CompositeDisposable compositeDisposable = new CompositeDisposable();
    
    private void doWork() {
        Disposable disposable = observable.subscribe(...);
        compositeDisposable.add(disposable);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        compositeDisposable.clear(); // 取消所有订阅
    }
    
  2. 选择正确的类型

    • 用 Observable 处理少量数据(<1000)或不涉及背压的场景。
    • 用 Flowable 处理大量数据、网络请求或可能产生背压的场景。
    • 用 Single 处理单次异步操作(如网络请求)。
    • 用 Completable 处理不关心返回值,只关心成功/失败的操作。
  3. 线程选择

    • I/O 工作用 Schedulers.io()
    • 计算工作用 Schedulers.computation()
    • UI 更新用 AndroidSchedulers.mainThread()
  4. 链式调用: RxJava 的链式调用非常强大,尽量保持链的完整性,使其清晰易读。

一、RxAndroid - Android 专用扩展

RxAndroid 是 RxJava 在 Android 平台上的扩展,提供了 Android 特有的功能。

1. AndroidSchedulers

java

implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'

核心功能

  • AndroidSchedulers.mainThread():在 Android 主线程执行操作
  • AndroidSchedulers.from(Looper looper):在指定 Looper 的线程执行

用例

java

// 网络请求后更新 UI
apiService.getUserData()
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread()) // 切换到主线程更新 UI
    .subscribe(user -> {
        textView.setText(user.getName());
        progressBar.setVisibility(View.GONE);
    });

2. Android 生命周期绑定

避免 Activity/Fragment 销毁后仍执行 UI 更新导致崩溃。

java

// 使用 RxLifecycle (需要额外依赖)
implementation 'com.trello.rxlifecycle4:rxlifecycle-android:4.0.2'
implementation 'com.trello.rxlifecycle4:rxlifecycle-components:4.0.2'

public class MainActivity extends RxAppCompatActivity {
    
    private void loadData() {
        apiService.getData()
            .compose(bindToLifecycle()) // 自动在 onDestroy 时取消订阅
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(data -> updateUI(data));
    }
    
    // 或者更精确的控制
    private void loadDataPrecise() {
        apiService.getData()
            .compose(bindUntilEvent(ActivityEvent.DESTROY)) // 直到 onDestroy
            .subscribe(...);
    }
}

二、RxBinding - Android UI 事件响应式

将 Android UI 组件的事件转换为 Observable。

1. 基础使用

java

implementation 'com.jakewharton.rxbinding4:rxbinding:4.0.0'
implementation 'com.jakewharton.rxbinding4:rxbinding-core:4.0.0'
implementation 'com.jakewharton.rxbinding4:rxbinding-appcompat:4.0.0'
// 其他模块:rxbinding-material, rxbinding-recyclerview, 等

2. 详细用例

按钮点击

java

RxView.clicks(buttonSearch)
    .throttleFirst(500, TimeUnit.MILLISECONDS) // 防抖,500ms内只响应一次
    .subscribe(unit -> performSearch());

// 长按事件
RxView.longClicks(buttonDelete)
    .subscribe(unit -> showDeleteConfirmation());

文本输入监听

java

RxTextView.textChanges(editTextSearch)
    .debounce(300, TimeUnit.MILLISECONDS) // 延迟300ms,避免频繁请求
    .filter(text -> text.length() > 2) // 至少3个字符才搜索
    .distinctUntilChanged() // 文本相同时不重复搜索
    .switchMap(query -> searchApi.search(query.toString())) // 取消之前的请求
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(results -> updateSearchResults(results));

多个输入验证

java

Observable.combineLatest(
    RxTextView.textChanges(editTextEmail).map(CharSequence::toString),
    RxTextView.textChanges(editTextPassword).map(CharSequence::toString),
    RxTextView.textChanges(editTextConfirmPassword).map(CharSequence::toString),
    (email, password, confirmPassword) -> {
        // 验证逻辑
        return isValidEmail(email) && 
               password.length() >= 6 && 
               password.equals(confirmPassword);
    }
).subscribe(isValid -> {
    buttonRegister.setEnabled(isValid);
    buttonRegister.setAlpha(isValid ? 1.0f : 0.5f);
});

RecyclerView 点击事件

java

// 需要 rxbinding-recyclerview
RxRecyclerView.itemClicks(recyclerView)
    .subscribe(position -> {
        Item item = adapter.getItem(position);
        openItemDetail(item);
    });

// 长按事件
RxRecyclerView.itemLongClicks(recyclerView)
    .subscribe(position -> showContextMenu(position));

ViewPager 页面切换

java

RxViewPager.pageSelections(viewPager)
    .subscribe(position -> {
        updateToolbarTitle(position);
        updateTabSelection(position);
    });

三、Retrofit + RxJava - 网络请求黄金组合

1. 配置

java

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'

2. API 接口定义

java

public interface GitHubService {
    
    // 返回 Observable
    @GET("users/{user}/repos")
    Observable<List<Repository>> listRepos(@Path("user") String user);
    
    // 返回 Single (更适合单次请求)
    @GET("users/{user}")
    Single<User> getUser(@Path("user") String userName);
    
    // 返回 Completable (不关心返回数据)
    @PUT("users/{user}/profile")
    Completable updateProfile(@Path("user") String user, @Body Profile profile);
    
    // 分页请求
    @GET("search/repositories")
    Observable<SearchResult> searchRepos(
        @Query("q") String query,
        @Query("page") int page,
        @Query("per_page") int perPage
    );
}

3. 复杂网络请求用例

顺序请求(后一个请求依赖前一个的结果):

java

// 先获取用户信息,再获取用户的仓库列表
apiService.getUser("octocat")
    .flatMap(user -> apiService.listRepos(user.getLogin()))
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(repos -> {
        showRepositories(repos);
    }, error -> {
        showError(error);
    });

并行请求

java

Observable.zip(
    apiService.getUserProfile(userId),
    apiService.getUserRepos(userId),
    apiService.getUserFollowers(userId),
    (profile, repos, followers) -> {
        // 合并三个请求的结果
        UserDetail userDetail = new UserDetail();
        userDetail.setProfile(profile);
        userDetail.setRepos(repos);
        userDetail.setFollowers(followers);
        return userDetail;
    }
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(userDetail -> {
    updateUserInterface(userDetail);
});

请求重试与超时

java

apiService.getData()
    .timeout(10, TimeUnit.SECONDS) // 10秒超时
    .retryWhen(errors -> errors
        .zipWith(Observable.range(1, 3), (throwable, attempt) -> {
            // 最多重试3次
            if (attempt > 3 || !(throwable instanceof SocketTimeoutException)) {
                throw Exceptions.propagate(throwable);
            }
            return attempt;
        })
        .flatMap(attempt -> Observable.timer(attempt * 2, TimeUnit.SECONDS))
    )
    .subscribe(...);

四、Room + RxJava - 数据库响应式操作

1. 配置

java

implementation "androidx.room:room-runtime:2.4.2"
implementation "androidx.room:room-rxjava3:2.4.2"
kapt "androidx.room:room-compiler:2.4.2"

2. DAO 定义

java

@Dao
public interface UserDao {
    
    // 插入返回 Completable
    @Insert
    Completable insertUser(User user);
    
    // 更新返回 Single<Integer> (受影响的行数)
    @Update
    Single<Integer> updateUser(User user);
    
    // 查询返回 Flowable,数据库变化时自动发射新数据
    @Query("SELECT * FROM users")
    Flowable<List<User>> getAllUsers();
    
    // 查询返回 Maybe (可能为空)
    @Query("SELECT * FROM users WHERE id = :userId")
    Maybe<User> getUserById(String userId);
    
    // 复杂查询
    @Query("SELECT * FROM users WHERE name LIKE :query OR email LIKE :query")
    Flowable<List<User>> searchUsers(String query);
}

3. 数据库操作用例

数据变化自动更新 UI

java

@Inject
UserDao userDao;

public void observeUsers() {
    userDao.getAllUsers()
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(users -> {
            // 当数据库中的用户数据发生变化时自动调用
            adapter.setUsers(users);
        });
}

// 添加新用户
public void addUser(User user) {
    userDao.insertUser(user)
        .subscribeOn(Schedulers.io())
        .subscribe(() -> {
            // 插入成功,上面的 observeUsers 会自动触发更新
            Log.d("Database", "User inserted successfully");
        });
}

搜索功能

java

RxTextView.textChanges(editTextSearch)
    .debounce(300, TimeUnit.MILLISECONDS)
    .switchMap(query -> {
        if (query.length() == 0) {
            return userDao.getAllUsers().toObservable();
        } else {
            return userDao.searchUsers("%" + query + "%").toObservable();
        }
    })
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .subscribe(users -> adapter.setUsers(users));

五、RxPermissions - 权限管理

简化 Android 运行时权限申请。

1. 配置

java

implementation 'com.github.tbruyelle:rxpermissions:0.12'

2. 用例

java

public class MainActivity extends AppCompatActivity {
    
    private RxPermissions rxPermissions;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        rxPermissions = new RxPermissions(this);
        
        setupPermissions();
    }
    
    private void setupPermissions() {
        // 单个权限申请
        RxView.clicks(buttonCamera)
            .compose(rxPermissions.ensure(Manifest.permission.CAMERA))
            .subscribe(granted -> {
                if (granted) {
                    openCamera();
                } else {
                    showPermissionDeniedDialog();
                }
            });
        
        // 多个权限同时申请
        RxView.clicks(buttonLocation)
            .compose(rxPermissions.ensure(
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_COARSE_LOCATION
            ))
            .subscribe(granted -> {
                if (granted) {
                    startLocationTracking();
                } else {
                    Toast.makeText(this, "需要位置权限", Toast.LENGTH_SHORT).show();
                }
            });
        
        // 复杂的权限申请流程
        RxView.clicks(buttonUpload)
            .compose(rxPermissions.ensure(Manifest.permission.READ_EXTERNAL_STORAGE))
            .flatMap(granted -> {
                if (!granted) throw new PermissionDeniedException();
                return Observable.just(true);
            })
            .flatMap(ignored -> apiService.uploadFile(file))
            .subscribe(
                result -> showUploadSuccess(),
                error -> {
                    if (error instanceof PermissionDeniedException) {
                        showPermissionExplanation();
                    } else {
                        showUploadError(error);
                    }
                }
            );
    }
}

六、其他有用的 RxJava 衍生库

1. RxKotlin

为 Kotlin 提供更友好的语法糖。

kotlin

implementation "io.reactivex.rxjava3:rxkotlin:3.0.1"

// Kotlin 扩展函数让代码更简洁
Observable.just("Hello", "World")
    .filter { it.length > 3 }
    .map { it.toUpperCase() }
    .subscribeBy(  // 命名参数,更清晰
        onNext = { println(it) },
        onError = { it.printStackTrace() },
        onComplete = { println("Done!") }
    )

2. RxRelay

类似于 Subjects,但不接受 onComplete 和 onError,适合作为事件总线。

java

implementation 'com.jakewharton.rxrelay3:rxrelay:3.0.1'

public class RxBus {
    private final PublishRelay<Object> bus = PublishRelay.create();
    
    public void send(Object event) {
        bus.accept(event);
    }
    
    public <T> Observable<T> toObservable(Class<T> eventType) {
        return bus.ofType(eventType);
    }
}

// 使用:不会因为 onError/onComplete 而终止
rxBus.send(new UserLoginEvent(user));

3. RxImagePicker

响应式图片选择。

java

RxImagePicker.with(activity).gallery().start()
    .subscribe(uri -> {
        // 处理选择的图片
        loadImage(uri);
    });

七、综合实战用例

1. 完整的登录流程

java

public class LoginViewModel extends ViewModel {
    
    private CompositeDisposable disposables = new CompositeDisposable();
    
    public void login(String email, String password) {
        disposables.add(
            Observable.combineLatest(
                RxTextView.textChanges(editTextEmail).map(CharSequence::toString),
                RxTextView.textChanges(editTextPassword).map(CharSequence::toString),
                (email, password) -> new LoginForm(email, password)
            )
            .filter(form -> isValidForm(form))
            .take(1) // 只取第一次有效的表单
            .switchMap(form -> 
                apiService.login(form.getEmail(), form.getPassword())
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
            )
            .doOnSubscribe(disposable -> showLoading())
            .doOnTerminate(() -> hideLoading())
            .subscribe(
                user -> {
                    // 登录成功
                    saveUser(user);
                    navigateToHome();
                },
                error -> {
                    // 登录失败
                    showLoginError(error);
                }
            )
        );
    }
    
    @Override
    protected void onCleared() {
        super.onCleared();
        disposables.clear();
    }
}

2. 实时搜索 + 缓存

java

public class SearchViewModel extends ViewModel {
    
    private BehaviorSubject<String> searchQuery = BehaviorSubject.create();
    private Observable<List<SearchResult>> searchResults;
    
    public SearchViewModel() {
        searchResults = searchQuery
            .debounce(400, TimeUnit.MILLISECONDS)
            .filter(query -> query.length() >= 2)
            .distinctUntilChanged()
            .switchMap(query -> 
                apiService.search(query)
                    .onErrorResumeNext(throwable -> {
                        // 网络失败时从缓存读取
                        return cacheService.getCachedResults(query).toObservable();
                    })
                    .doOnNext(results -> cacheService.cacheResults(query, results))
            )
            .replay(1) // 缓存最近一次结果
            .autoConnect();
    }
    
    public void search(String query) {
        searchQuery.onNext(query);
    }
    
    public Observable<List<SearchResult>> getSearchResults() {
        return searchResults;
    }
}

八、最佳实践总结

  1. 内存管理:始终使用 CompositeDisposable 管理订阅,在合适的生命周期取消订阅
  2. 错误处理:不要忽略 onError,提供适当的错误处理机制
  3. 背压策略:大数据流使用 Flowable 并选择合适的背压策略
  4. 线程调度:合理使用 subscribeOn 和 observeOn
  5. 操作符选择:理解各操作符的区别(如 flatMap vs concatMap vs switchMap
  6. 测试:使用 TestScheduler 和 TestObserver 进行单元测试

这些衍生库和组合让 RxJava 在 Android 开发中变得更加强大和实用,能够优雅地处理复杂的异步场景和事件流。