rxjava-migration
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRxJava Migration
RxJava 迁移
Migrate RxJava to Kotlin coroutines/flows incrementally. Only invoke this skill when the user explicitly asks to migrate RxJava code. Simple cases map directly; complex cases need a strategy and user input before any code is written.
逐步将RxJava迁移到Kotlin协程/Flow。仅当用户明确要求迁移RxJava代码时才调用此技能。简单场景可直接映射;复杂场景则需要先制定策略并获取用户输入,再进行代码编写。
Step 1: Complexity gate
步骤1:复杂度判断
Classify before writing any migrated code. A chain is Complex if ANY of: nested / with an inner /; a custom ; complex error recovery (, backoff, retry counts); multi-source / (3+ sources); with an explicit backpressure strategy; a shared across classes; or an unclear API return type. Any single Complex criterion makes the whole chain Complex — a two-operator chain with is Complex.
flatMapswitchMapSingleObservableSchedulerretryWhenzipcombineLatestFlowableSubjectretryWhenAlways confirm what each called API returns before migrating — do not assume. A leaf you think is migrated may still return /.
SingleObservable在编写迁移代码前先进行分类。如果满足以下任一条件,链式调用即为复杂场景:嵌套的/且内部包含/;自定义;复杂错误恢复(、退避策略、重试次数);多源/(3个及以上源);带有显式背压策略的;跨类共享的;或不明确的API返回类型。只要满足任一复杂场景判定条件,整个链式调用就属于复杂场景——比如包含的两操作符链式调用也属于复杂场景。
flatMapswitchMapSingleObservableSchedulerretryWhenzipcombineLatestFlowableSubjectretryWhen迁移前务必确认每个被调用API的返回类型——不要假设。你认为已完成迁移的叶子节点可能仍返回/。
SingleObservableType mapping (state it explicitly — don't silently transform)
类型映射(需明确说明,不要静默转换)
| RxJava | Coroutines/Flow |
|---|---|
| |
| |
| |
| ask — |
BehaviorSubjectStateFlow.valueSharedFlow(replay = 1).value.value| RxJava | 协程/Flow |
|---|---|
| |
| |
| |
| 需询问 — |
BehaviorSubjectStateFlow.valueSharedFlow(replay = 1).value.valueSchedulers and the observeOn
shift
observeOnScheduler 与 observeOn
的转变
observeOnSchedulers.io()Dispatchers.IOcomputation()DefaultmainThread()Mainsingle()newSingleThreadContext(...)newThread()IO- does NOT become a dispatcher switch in the repository/use case — it means the caller collects on
observeOn(AndroidSchedulers.mainThread()), which in the coroutines model is the ViewModel's job (Mainruns onviewModelScope). Explain this shift to the developer.Main - When the called API is already a , it manages its own dispatcher via
suspend fun—withContexthas no equivalent and simply disappears. Do not wrap an already-main-safe suspend function in anothersubscribeOn.withContext - +
subscribeOn→observeOnapplies upstream only; restructure accordingly.flowOn
For the full operator mapping, see .
migration-map.mdSchedulers.io()Dispatchers.IOcomputation()DefaultmainThread()Mainsingle()newSingleThreadContext(...)newThread()IO- 在仓库/用例层不会转换为调度器切换,这意味着调用者需在
observeOn(AndroidSchedulers.mainThread())调度器上收集流,而在协程模型中这是ViewModel的职责(Main运行在viewModelScope调度器上)。需向开发者解释这一转变。Main - 当被调用API已是时,它会通过
suspend fun管理自身的调度器——withContext没有等效操作,直接移除即可。不要将已主线程安全的suspend函数再包裹在另一个subscribeOn中。withContext - +
subscribeOn→observeOn仅作用于上游;需相应调整结构。flowOn
完整的操作符映射请查看。
migration-map.mdRetry — the accuracy trap
重试——准确性陷阱
RxJava's is stateful; Flow's predicate receives the , not an index.
retryWhenretryThrowablekotlin
// WRONG — 'attempt' is a Throwable, not a Long; this does not compile
flow.retry(3) { attempt -> delay(attempt * 1000L); true }
// CORRECT — retry(n)'s predicate gets the cause
flow.retry(3) { cause -> cause is IOException }
// CORRECT — stateful backoff: attempt IS the 0-based index here
flow.retryWhen { cause, attempt ->
if (cause is IOException && attempt < 3) { delay((attempt + 1) * 1000L); true } else false
}Use for any policy that depends on the attempt count.
retryWhen { cause, attempt -> }RxJava的是有状态的;Flow的谓词接收的是****,而非索引。
retryWhenretryThrowablekotlin
// WRONG — 'attempt' is a Throwable, not a Long; this does not compile
flow.retry(3) { attempt -> delay(attempt * 1000L); true }
// CORRECT — retry(n)'s predicate gets the cause
flow.retry(3) { cause -> cause is IOException }
// CORRECT — stateful backoff: attempt IS the 0-based index here
flow.retryWhen { cause, attempt ->
if (cause is IOException && attempt < 3) { delay((attempt + 1) * 1000L); true } else false
}对于任何依赖重试次数的策略,请使用。
retryWhen { cause, attempt -> }Interop during incremental migration
增量迁移期间的互操作
Add (or ); keep interop at layer boundaries only, never mixing RxJava and coroutines inside one function body.
kotlinx-coroutines-rx3rx2kotlin
observable.asFlow(); single.await(); maybe.awaitSingleOrNull(); completable.await() // Rx → coroutines
flow.asObservable(); flow.asSingle() // coroutines → Rx (asSingle throws if the flow emits 0 or 2+ elements)Migrate leaf-up (data source → repository → use case → ViewModel), remove each bridge once its layer is fully migrated, and commit per layer.
添加(或);仅在层边界使用互操作,切勿在单个函数体内混合RxJava和协程。
kotlinx-coroutines-rx3rx2kotlin
observable.asFlow(); single.await(); maybe.awaitSingleOrNull(); completable.await() // Rx → coroutines
flow.asObservable(); flow.asSingle() // coroutines → Rx (asSingle throws if the flow emits 0 or 2+ elements)从叶子节点向上迁移(数据源 → 仓库 → 用例 → ViewModel),每层完全迁移后移除对应的桥接代码,并按层提交。
Complex cases — stop and ask before migrating
复杂场景——迁移前先暂停并询问
retryWhenSchedulerCoroutineDispatcherFlowablebufferconflateDROP_OLDESTflatMapswitchMapswitchMapflatMapLatestSubjectStateFlowSharedFlowonErrorResumeNextcatchcatch { e -> if (e is CancellationException) throw e else emit(fallback) }( in → delete it entirely: is cancelled automatically when the ViewModel is cleared.)
compositeDisposable.clear()onCleared()viewModelScoperetryWhenSchedulerCoroutineDispatcherFlowablebufferconflateDROP_OLDESTflatMapswitchMapswitchMapflatMapLatestSubjectStateFlowSharedFlowonErrorResumeNextcatchcatch { e -> if (e is CancellationException) throw e else emit(fallback) }(中的 → 直接删除:当ViewModel被销毁时,会自动取消。)
onCleared()compositeDisposable.clear()viewModelScope