1. 什么是 RxJava?
RxJava 是 Reactive Extensions 在 JVM 上的一个实现,它是一个基于观察者模式、用于构建异步和基于事件的程序的库。其核心思想是:异步数据流编程。
你可以将任何东西都看作数据流:变量、用户输入、属性、缓存、网络请求返回的数据等。RxJava 提供了强大的工具(操作符)来对这些数据流进行创建、转换、组合和过滤。
2. 核心概念
RxJava 2.x/3.x 的核心构建块是以下几个接口:
-
Observable(可观察者) : 数据的生产者,它可以发射0个、1个或多个数据项,也可以发射一个错误或完成信号。它不支持背压。 -
Observer(观察者) : 数据的消费者,它订阅Observable并对其发出的事件做出反应。onSubscribe(Disposable d): 建立订阅关系时调用。onNext(T t): 接收到一个数据项时调用。onError(Throwable e): 当Observable发生错误时调用。onComplete(): 当Observable完成所有数据发射时调用。
-
Flowable: 与Observable类似,但支持背压。当生产者的发射速度超过消费者的处理速度时,用于处理流量控制。适用于大量数据或网络请求等场景。 -
Single: 只发射一个数据项或一个错误信号。类似于Promise。适用于网络请求等单次异步操作。onSuccess(T t)onError(Throwable e)
-
Completable: 不发射任何数据,只处理完成或错误信号。适用于只知道操作是否成功,而不关心返回值的场景(如:缓存写入)。 -
Maybe: 可能发射0个或1个数据项,或者一个错误/完成信号。是Single和Completable的结合。 -
Disposable: 订阅的凭证,用于取消订阅,防止内存泄漏。 -
Scheduler(调度器): 用于控制异步操作的线程。
二、原理
1. 观察者模式
RxJava 的核心是观察者模式的扩展。
- 被观察者 (Observable) 和 观察者 (Observer) 通过
subscribe方法建立订阅关系。 - 一旦订阅建立,
Observable就可以向Observer推送数据。 - 这是一种 “推” 的模式,而不是传统的 “拉” 的模式。
2. 事件流
一个 Observable 的生命周期由三种类型的事件组成(遵循约定):
onNext: 可以多次调用,每次传递一个数据。onError或onComplete: 只能调用其中一个,并且是终止性事件,调用后不能再发送任何事件。
3. 背压 (Backpressure)
问题: 当生产者 (Observable) 发射数据的速度远快于消费者 (Observer) 处理数据的速度时,会导致未处理的数据在内存中堆积,最终导致 OutOfMemoryError。
解决方案:
-
使用
Flowable代替Observable。Flowable实现了 响应式流规范,提供了多种背压策略: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(如List,Set)创建。java
List<String> list = Arrays.asList("Apple", "Banana", "Orange"); Observable.fromIterable(list).subscribe(System.out::println); -
create(): 通过手动调用onNext,onError,onComplete来创建。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());
});
六、总结与最佳实践
-
内存泄漏: 在 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(); // 取消所有订阅 } -
选择正确的类型:
- 用
Observable处理少量数据(<1000)或不涉及背压的场景。 - 用
Flowable处理大量数据、网络请求或可能产生背压的场景。 - 用
Single处理单次异步操作(如网络请求)。 - 用
Completable处理不关心返回值,只关心成功/失败的操作。
- 用
-
线程选择:
- I/O 工作用
Schedulers.io() - 计算工作用
Schedulers.computation() - UI 更新用
AndroidSchedulers.mainThread()
- I/O 工作用
-
链式调用: 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;
}
}
八、最佳实践总结
- 内存管理:始终使用
CompositeDisposable管理订阅,在合适的生命周期取消订阅 - 错误处理:不要忽略
onError,提供适当的错误处理机制 - 背压策略:大数据流使用
Flowable并选择合适的背压策略 - 线程调度:合理使用
subscribeOn和observeOn - 操作符选择:理解各操作符的区别(如
flatMapvsconcatMapvsswitchMap) - 测试:使用
TestScheduler和TestObserver进行单元测试
这些衍生库和组合让 RxJava 在 Android 开发中变得更加强大和实用,能够优雅地处理复杂的异步场景和事件流。