RxJava 在國內開發社群中廣受歡迎,但其內部原理往往讓初學者望而卻步。本文將通過一個「尋找最萌貓咪」的實戰案例,層層遞進地拆解 RxJava 的核心設計思想——從同步阻塞、異步回調、接口封裝,到 AsyncJob 抽象、map/flatMap 運算子,最終引入 RxJava 的 Observable/Observer/subscribe 模型,幫助你建立清晰的心智模型。
假設我們有一個 Web API,能根據關鍵字搜索全網貓咪圖片,每張圖片帶有「可愛指數」(整數)。任務是:下載貓咪列表 → 選出最萌的一隻 → 保存到本地 → 返回存儲路徑。
// 實體類
public class Cat implements Comparable<Cat> {
String image;
int cuteness;
@Override public int compareTo(Cat another) {
return Integer.compare(cuteness, another.cuteness);
}
}
// API 接口(同步版)
public interface Api {
List<Cat> queryCats(String query);
Uri store(Cat cat);
}
// 業務邏輯
public class CatsHelp {
Api api;
public Uri saveTheCutestCat(String query) {
List<Cat> cats = api.queryCats(query);
Cat cutest = Collections.max(cats);
return api.store(cutest);
}
}
代碼簡潔直觀,但網絡請求與 I/O 操作若同步執行會阻塞線程,實際開發中必須異步化。
為了非阻塞,API 改為接口回調風格:
public interface Api {
interface CatsQueryCallback {
void onCatListReceived(List<Cat> cats);
void onCatsQueryError(Exception e);
}
interface StoreCallback {
void onCateStored(Uri uri);
void onStoreFailed(Exception e);
}
void queryCats(String query, CatsQueryCallback cb);
void store(Cat cat, StoreCallback cb);
}
業務邏輯被迫層層嵌套:
public class CatsHelp {
Api api;
public void saveTheCutestCat(String query, CutestCatCallback cb) {
api.queryCats(query, new CatsQueryCallback() {
@Override public void onCatListReceived(List<Cat> cats) {
Cat cutest = Collections.max(cats);
api.store(cutest, new StoreCallback() {
@Override public void onCateStored(Uri uri) {
cb.onCutestCatSaved(uri);
}
@Override public void onStoreFailed(Exception e) {
cb.onCutestCatFail(e);
}
});
}
@Override public void onCatsQueryError(Exception e) {
cb.onCutestCatFail(e);
}
});
}
}
代碼縱向膨脹,錯誤處理分散,維護極其困難——這就是典型的「回調地獄」。
觀察三個回調接口(CutestCatCallback、CatsQueryCallback、StoreCallback)結構相似:皆含「成功攜帶泛型結果」與「失敗攜帶異常」。提取通用接口:
public interface Callback<T> {
void onResult(T result);
void onError(Exception e);
}
API 與業務邏輯隨之簡化:
public interface Api {
void queryCats(String query, Callback<List<Cat>> cb);
void store(Cat cat, Callback<Uri> cb);
}
public class CatsHelp {
Api api;
public void saveTheCutestCat(String query, Callback<Uri> cb) {
api.queryCats(query, new Callback<List<Cat>>() {
@Override public void onResult(List<Cat> cats) {
Cat cutest = Collections.max(cats);
api.store(cutest, new Callback<Uri>() {
@Override public void onResult(Uri uri) { cb.onResult(uri); }
@Override public void onError(Exception e) { cb.onError(e); }
});
}
@Override public void onError(Exception e) { cb.onError(e); }
});
}
}
雖然接口統一了,但「雙參數傳遞(查詢條件 + 回調)」與「嵌套調用」問題仍在。
靈感來自:若方法只需傳入查詢條件,返回一個「攜帶回調能力的對象」,調用者再決定何時啟動,豈不更靈活?定義 AsyncJob<T>:
public abstract class AsyncJob<T> {
public abstract void start(Callback<T> callback);
}
為適配原有 API,引入 ApiWrapper 將回調式方法包裝為 AsyncJob:
public class ApiWrapper {
Api api;
public AsyncJob<List<Cat>> queryCats(final String query) {
return new AsyncJob<List<Cat>>() {
@Override public void start(final Callback<List<Cat>> cb) {
api.queryCats(query, new Callback<List<Cat>>() {
@Override public void onResult(List<Cat> r) { cb.onResult(r); }
@Override public void onError(Exception e) { cb.onError(e); }
});
}
};
}
public AsyncJob<Uri> store(final Cat cat) { /* 類似實現 */ }
}
調用方式變為:
AsyncJob<List<Cat>> job = new ApiWrapper().queryCats("query");
job.start(new Callback<List<Cat>>() { /* 处理结果 */ });
業務邏輯隨之重構為鏈式組合:
public AsyncJob<Uri> saveTheCutestCat(final String query) {
AsyncJob<List<Cat>> catsJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestJob = new AsyncJob<Cat>() {
@Override public void start(Callback<Cat> cb) {
catsJob.start(new Callback<List<Cat>>() {
@Override public void onResult(List<Cat> r) { cb.onResult(Collections.max(r)); }
@Override public void onError(Exception e) { cb.onError(e); }
});
}
};
AsyncJob<Uri> storedJob = new AsyncJob<Uri>() {
@Override public void start(Callback<Uri> cb) {
cutestJob.start(new Callback<Cat>() {
@Override public void onResult(Cat c) { apiWrapper.store(c).start(cb); }
@Override public void onError(Exception e) { cb.onError(e); }
});
}
};
return storedJob;
}
上述代碼中,cutestJob 本質是將 AsyncJob<List<Cat>> 轉換為 AsyncJob<Cat>(同步純函數轉換);storedJob 則是將 AsyncJob<Cat> 轉換為 AsyncJob<Uri>(異步操作轉換)。提取兩種通用模式:
T → R,定義 Func<T, R> { R call(T t); }T → AsyncJob<R>,定義 Func<T, AsyncJob<R>> { AsyncJob<R> call(T t); }在 AsyncJob 中實現:
public <R> AsyncJob<R> map(final Func<T, R> func) {
final AsyncJob<T> source = this;
return new AsyncJob<R>() {
@Override public void start(final Callback<R> cb) {
source.start(new Callback<T>() {
@Override public void onResult(T r) { cb.onResult(func.call(r)); }
@Override public void onError(Exception e) { cb.onError(e); }
});
}
};
}
public <R> AsyncJob<R> flatMap(final Func<T, AsyncJob<R>> func) {
final AsyncJob<T> source = this;
return new AsyncJob<R>() {
@Override public void start(final Callback<R> cb) {
source.start(new Callback<T>() {
@Override public void onResult(T r) { func.call(r).start(cb); }
@Override public void onError(Exception e) { cb.onError(e); }
});
}
};
}
業務邏輯終於變得極其優雅:
public AsyncJob<Uri> saveTheCutestCat(final String query) {
return apiWrapper.queryCats(query)
.map(new Func<List<Cat>, Cat>() {
@Override public Cat call(List<Cat> cats) { return Collections.max(cats); }
})
.flatMap(new Func<Cat, AsyncJob<Uri>>() {
@Override public AsyncJob<Uri> call(Cat cat) { return apiWrapper.store(cat); }
});
}
對照 RxJava 核心概念:
| 自定義概念 | RxJava 對應 |
|---|---|
AsyncJob<T> | Observable<T> |
Callback<T> | Observer<T> / Subscriber<T> |
start() | subscribe() |
map | map |
flatMap | flatMap |
將 ApiWrapper、CatsHelp 改用 RxJava 1.x API:
public class ApiWrapper {
Api api;
public Observable<List<Cat>> queryCats(final String query) {
return Observable.create(subscriber -> {
api.queryCats(query, new CatsQueryCallback() {
@Override public void onCatListReceived(List<Cat> cats) { subscriber.onNext(cats); }
@Override public void onCatsQueryError(Exception e) { subscriber.onError(e); }
});
});
}
public Observable<Uri> store(final Cat cat) { /* 類似 */ }
}
public class CatsHelp {
ApiWrapper apiWrapper;
public Observable<Uri> saveTheCutestCat(String query) {
return apiWrapper.queryCats(query)
.map(cats -> Collections.max(cats))
.flatMap(cat -> apiWrapper.store(cat));
}
}
public class Test {
public static void main(String[] args) {
new CatsHelp().saveTheCutestCat("ss")
.subscribe(uri -> System.out.println("Saved: " + uri),
e -> e.printStackTrace(),
() -> System.out.println("Completed"));
}
}
至此,RxJava 的核心原理——將異步事件流建模為可組合的序列,通過運算子實現聲明式轉換與合併——已呼之欲出。理解這層本質,面對複雜的響應式鏈路也能游刃有餘。
長路漫漫,共勉!