任何框架都是從無到有退疫,都是為了解決問題而產(chǎn)生,那么RxJava是如何產(chǎn)生的呢勾哩?RxJava代碼的寫法抗蠢,為何如此讓人看不懂,回調(diào)的參數(shù)等等思劳,讓小白看了摸不著頭腦迅矛。
接下來的文章,主要是依據(jù)NotRxJava懶人專用指南,結(jié)合自己的理解潜叛,寫的一篇更加小白的文章秽褒,以幫助我們更好的梳理和理解。
Cat 應(yīng)用程序
讓我們來創(chuàng)建一個真實世界的例子威兜。我們都知道貓是我們技術(shù)發(fā)展的引擎销斟,所以就讓我們也來創(chuàng)建這么一個用來下載貓圖片的典型應(yīng)用吧。
任務(wù)描述
我們有個 Web API椒舵,能根據(jù)給定的查詢請求搜索到整個互聯(lián)網(wǎng)上貓的圖片蚂踊。每個圖片包含可愛指數(shù)的參數(shù)(描述圖片可愛度的整型值)。我們的任務(wù)將會下載到一個貓列表的集合笔宿,選擇最可愛的那個犁钟,然后把它保存到本地。
我們只關(guān)心下載泼橘、處理和保存貓的數(shù)據(jù)涝动。
我們開始吧~
思維導(dǎo)圖
這里各個點(diǎn)后面都會結(jié)合這個圖,進(jìn)行解說
模型和 API
根據(jù)我們的任務(wù)炬灭,我們能夠得到的信息
- 給定的查詢信息String query
- 搜索所有的貓圖片 List<Cat> queryCats(String query)
- 每個圖片包含可愛指數(shù)的參數(shù)(描述圖片可愛度的整型值)找到最可愛的貓 (Cat 包含圖片Bitmap image和可愛屬性int cuteness)醋粟。
- 最可愛的貓圖片保存到本地(Uri store(Cat cat),不清楚Uri 的自行查閱)
“貓”的數(shù)據(jù)結(jié)構(gòu):
public class Cat implements Comparable<Cat> {
//圖片
Bitmap image;
//可愛屬性
int cuteness;
//為了實現(xiàn)比較功能,我們的Cat對象需要實現(xiàn)Comparable<Cat>接口昔穴,然后重寫compareTo方法
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public int compareTo(@NonNull Cat another) {
//這里大小比較只針對可愛屬性 cuteness
return Integer.compare(cuteness, another.cuteness);
}
}
我們先不考慮異步情況镰官,那么我們的接口Api:
public interface Api {
//阻塞
List<Cat> queryCats(String query);
Uri store(Cat cat);
}
然后吧我們的業(yè)務(wù)邏輯封裝到我們的helper類
public class CatsHelper {
Api api;
//組合方法
public Uri saveTheCutestCat(String query){
List<Cat> cats = api.queryCats(query);
Cat cat = findCutestCat(cats);
Uri uri = api.store(cat);
return uri;
}
private Cat findCutestCat(List<Cat> cats) {
return Collections.max(cats);
}
}
我們知道,在實際應(yīng)用之中吗货,我們會處理很多任務(wù)泳唠,如果使用上面的阻塞方式,用戶的每次操作都需要等待返回之后才能繼續(xù)宙搬,這明顯是不好的笨腥,因此我們需要異步。
異步Api接口
我們queryCats是一個http請求勇垛,搜索完畢脖母,需要有一個回調(diào)告訴我們,以讓我們處理數(shù)據(jù)闲孤,并進(jìn)行下步業(yè)務(wù)邏輯谆级,于是,新的api的代碼如下:
public interface Api {
//阻塞api
List<Cat> queryCats(String query);
Uri store(Cat cat);
//異步api讼积,這里就不需要返回值了肥照,直接在回調(diào)里獲取即可
void queryCatsAsync(String query, CatsQueryCallback callback);
void store(Cat cat, StoreCallback storeCallback);
//請求的接口回調(diào)
interface CatsQueryCallback {
void onCatListReceived(List<Cat> cats);
void onError(Exception e);
}
//保存數(shù)據(jù)的接口回調(diào)
interface StoreCallback{
void onCatStored(Uri uri);
void onStoreFailed(Exception e);
}
}
我們的helper類,作出相應(yīng)的更改勤众,為了讓異步更加完整舆绎,我們比較最可愛的貓成功或者失敗,都需要告訴用戶们颜,于是也需要寫一個接口CutestCatCallback回調(diào)出去吕朵。
public class CatsHelper {
private Cat findCutestCat(List<Cat> cats) {
return Collections.max(cats);
}
//監(jiān)聽接口
public interface CutestCatCallback {
void onCutestCatSaved(Uri uri);
void onQueryFailed(Exception e);
}
Api api;
//組合方法
public Uri saveTheCutestCat(String query){
List<Cat> cats = api.queryCats(query);
Cat cat = findCutestCat(cats);
Uri uri = api.store(cat);
return uri;findCutestCat
}
//異步
public void saveTheCutestCatAsyc(final String query, final CutestCatCallback cutestCatCallback){
api.queryCatsAsync(query, new Api.CatsQueryCallback() {
@Override
public void onCatListReceived(List<Cat> cats) {
Cat cat = findCutestCat(cats);
api.store(cat, new Api.StoreCallback() {
@Override
public void onCatStored(Uri uri) {
cutestCatCallback.onCutestCatSaved(uri);
}
@Override
public void onStoreFailed(Exception e) {
cutestCatCallback.onQueryFailed(e);
}
});
}
@Override
public void onError(Exception e) {
cutestCatCallback.onQueryFailed(e);
}
});
}
}
現(xiàn)在它有了更多無關(guān)代碼和花括號,但是邏輯是一樣的窥突。
每一個異步操作努溃,我們都必須創(chuàng)建出回調(diào)接口并在代碼中手動的插入它們,接口也越來越多波岛!
在這樣的代碼中錯誤不會自動地傳遞茅坛,我們需要在手動在onStoreFailed和onQueryFailed方法里面,添加代碼则拷,將錯誤傳遞給上一層贡蓖。
為了解決這些問題,我們引入了泛型回調(diào)
泛型回調(diào)
泛型類
/**
* 定義泛型類
*
* 與普通類的定義相比煌茬,上面的代碼在類名后面多出了 <T1, T2>斥铺,
* T1, T2 是自定義的標(biāo)識符,也是參數(shù)坛善,用來傳遞數(shù)據(jù)的類型晾蜘,而不是數(shù)據(jù)的值邻眷,我們稱之為類型參數(shù)。
* 在泛型中剔交,不但數(shù)據(jù)的值可以通過參數(shù)傳遞肆饶,數(shù)據(jù)的類型也可以通過參數(shù)傳遞。
* T1, T2 只是數(shù)據(jù)類型的占位符岖常,運(yùn)行時會被替換為真正的數(shù)據(jù)類型驯镊。
* Created by philos on 17-9-16.
*/
public class Point<T1, T2>{
T1 x;
T2 y;
public T1 getX() {
return x;
}
public void setX(T1 x) {
this.x = x;
}
public T2 getY() {
return y;
}
public void setY(T2 y) {
this.y = y;
}
}
泛型接口
/**
* 定義泛型接口
* Created by philos on 17-9-16.
*/
interface Info<T> {
public T getVar();
}
接口實現(xiàn)類
/**
* 實現(xiàn)泛型接口
* Created by philos on 17-9-16.
*/
public class InfoImp<T> implements Info<T> {
private T var;
// 定義泛型構(gòu)造方法
public InfoImp(T var) {
this.setVar(var);
}
public void setVar(T var) {
this.var = var;
}
@Override
public T getVar() {
return this.var;
}
}
泛型方法(固定參數(shù) 和 可變參數(shù))
public class Main {
//泛型方法
public static <T> void out(T t) {
System.out.println(t);
}
//可變參數(shù) 泛型方法
public static <T> void out(T... args) {
for (T t : args) {
System.out.println(t);
}
}
public static void main(String[] args) {
//普通泛型
out("findingsea");
out(123);
out(11.11);
out(true);
//可變參數(shù)泛型
out("findingsea", 123, 11.11, true);
}
}
了解了上面具體的寫法,我們繼續(xù)改造我們的貓程序代碼竭鞍,請看上面的思維導(dǎo)圖左邊的異步優(yōu)化板惑。
泛型回調(diào)
對比接口可以發(fā)現(xiàn),回調(diào)情況有兩種偎快,成功和失敗冯乘,成功的返回一個對象,失敗的返回Exception類晒夹,于是新增統(tǒng)一的回調(diào)接口如下:
/**
* 泛型回調(diào)
* 找到共同的模式裆馒,進(jìn)行抽離
* 替代原來所有的接口
* Created by philos on 17-9-15.
*/
public interface Callback<T> {
void onResult(T result);
void onError(Exception e);
}
Api 包裝類ApiWrapper
我們的之前就定義完畢了,上線之后惋戏,一般不會進(jìn)行大幅度修改领追,這個時候,我們可以實現(xiàn)一個Api包裝類响逢。
/**
* 使用了泛型之后,兩個接口合并為一個接口
* Created by philos on 17-9-15.
*/
public class ApiWrapper {
Api api;
public void queryCats(String query, final Callback<List<Cat>> castCallback){
api.queryCatsAsync(query, new Api.CatsQueryCallback() {
@Override
public void onCatListReceived(List<Cat> cats) {
castCallback.onResult(cats);
}
@Override
public void onError(Exception e) {
castCallback.onError(e);
}
});
}
public void store(Cat cat, final Callback<Uri> uriCallback){
api.store(cat, new Api.StoreCallback() {
@Override
public void onCatStored(Uri uri) {
uriCallback.onResult(uri);
}
@Override
public void onStoreFailed(Exception e) {
uriCallback.onError(e);
}
});
}
}
我們對應(yīng)的helper類就不在持有Api對象了棕孙,換成了包裝類舔亭,代碼修改如下:
public class CatsHelper {
private Cat findCutestCat(List<Cat> cats) {
return Collections.max(cats);
}
//監(jiān)聽接口
public interface CutestCatCallback {
void onCutestCatSaved(Uri uri);
void onQueryFailed(Exception e);
}
//泛型回調(diào) (retrofit很像有木有)
ApiWrapper apiWrapper;
public void saveTheCutestCatWrap(final String query, final CutestCatCallback cutestCatCallback){
apiWrapper.queryCats(query, new Callback<List<Cat>>() {
@Override
public void onResult(List<Cat> result) {
Cat cat = findCutestCat(result);
apiWrapper.store(cat, new Callback<Uri>() {
@Override
public void onResult(Uri result) {
cutestCatCallback.onCutestCatSaved(result);
}
@Override
public void onError(Exception e) {
cutestCatCallback.onQueryFailed(e);
}
});
}
@Override
public void onError(Exception e) {
cutestCatCallback.onQueryFailed(e);
}
});
}
}
分解,使用臨時泛型對象
我們發(fā)現(xiàn)異步操作(queryCats蟀俊,queryCats钦铺,還有saveTheCutestCat),它們都遵循了相同的模式肢预。調(diào)用它們的方法有一些參數(shù)(query矛洞、cat)也包括一個回調(diào)對象。再次說明:任何異步操作需要攜帶所需的常規(guī)參數(shù)和一個回調(diào)實例對象烫映。請看思維導(dǎo)圖左邊異步優(yōu)化沼本。
臨時泛型對象
對外提供的方法名暫時起為start
/**
* 分解異步操作,每步有一個參數(shù)
* 每步返回锭沟,臨時對象
* 每步攜帶 回調(diào)信息
* 避免回調(diào)地獄抽兆,用鏈?zhǔn)浇鉀Q問題
* Created by philos on 17-9-16.
*/
public abstract class AsyncJob<T> {
//包裝了方法,對外隱藏族淮,外部只需傳入回調(diào)辫红,定義返回類型凭涂,調(diào)用是start即可
public abstract void start(Callback<T> callback);
}
改造wrapper類,把我們以前的方法也隱藏起來了
/**
* 所有的異步操作都統(tǒng)一了
* 每次異步操作都返回AsyncJob<T>
* 每次異步都攜帶 回調(diào)Callback<T>
* start包裝了真正的請求贴妻,外部不關(guān)心切油,只要調(diào)用start即可
* Created by philos on 17-9-16.
*/
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>> callback) {
//這里進(jìn)行請求,然后返回給callback就可以了
api.queryCatsAsync(query, new Api.CatsQueryCallback() {
@Override
public void onCatListReceived(List<Cat> cats) {
callback.onResult(cats);
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
}
public AsyncJob<Uri> store(final Cat cat){
return new AsyncJob<Uri>() {
@Override
public void start(final Callback<Uri> callback) {
//進(jìn)行請求名惩,然后返回給callback
api.store(cat, new Api.StoreCallback() {
@Override
public void onCatStored(Uri uri) {
callback.onResult(uri);
}
@Override
public void onStoreFailed(Exception e) {
callback.onError(e);
}
});
}
};
}
}
helper類也需要進(jìn)行修改
public class CatsHelper {
private Cat findCutestCat(List<Cat> cats) {
return Collections.max(cats);
}
//監(jiān)聽接口
public interface CutestCatCallback {
void onCutestCatSaved(Uri uri);
void onQueryFailed(Exception e);
}
//分解統(tǒng)一返回臨時對象,鏈?zhǔn)秸{(diào)用澎胡,高大上有木有
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(final String query){
//直接new 需要返回的對象
return new AsyncJob<Uri>() {
@Override
public void start(final Callback<Uri> callback) {
//這里請求數(shù)據(jù),返回, 每個異步都有自己的回調(diào)信息這里重新new 一個CallBack
apiWrapper.queryCats(query).start(new Callback<List<Cat>>() {
@Override
public void onResult(List<Cat> result) {
//進(jìn)行數(shù)據(jù)操作和轉(zhuǎn)換
Cat cat = findCutestCat(result);
apiWrapper.store(cat).start(new Callback<Uri>() {
@Override
public void onResult(Uri result) {
callback.onResult(result);
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
}
}
是我們的邏輯數(shù)據(jù)流:
(async) (sync) (async)
query ===========> List<Cat> -------------> Cat ==========> Uri
queryCats findCutest store
分解成更小的操作
public class CatsHelper {
private Cat findCutestCat(List<Cat> cats) {
return Collections.max(cats);
}
//監(jiān)聽接口
public interface CutestCatCallback {
void onCutestCatSaved(Uri uri);
void onQueryFailed(Exception e);
}
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
//獲取貓列表 返回臨時對象 catsListAsyncJob
final AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
//查找最可愛貓 返回臨時對象 cutestCatAsyncJob
final AsyncJob<Cat> cutestCatAsyncJob = new AsyncJob<Cat>() {
@Override
public void start(final Callback<Cat> callback) {
catsListAsyncJob.start(new Callback<List<Cat>>() {
@Override
public void onResult(List<Cat> result) {
callback.onResult(findCutestCat(result));
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
//保存貓到本地 返回臨時對象 storedUriAsyncJob
AsyncJob<Uri> storedUriAsyncJob = new AsyncJob<Uri>() {
@Override
public void start(final Callback<Uri> cutestCatCallback) {
cutestCatAsyncJob.start(new Callback<Cat>() {
@Override
public void onResult(Cat cutest) {
apiWrapper.store(cutest)
.start(new Callback<Uri>() {
@Override
public void onResult(Uri result) {
cutestCatCallback.onResult(result);
}
@Override
public void onError(Exception e) {
cutestCatCallback.onError(e);
}
});
}
@Override
public void onError(Exception e) {
cutestCatCallback.onError(e);
}
});
}
};
return storedUriAsyncJob;
}
映射 (其實就是操作符的封裝)
現(xiàn)在看看 AsyncJob<Cat> cutestCatAsyncJob的部分:
AsyncJob<Cat> cutestCatAsyncJob = new AsyncJob<Cat>() {
@Override
public void start(Callback<Cat> callback) {
catsListAsyncJob.start(new Callback<List<Cat>>() {
@Override
public void onResult(List<Cat> result) {
callback.onResult(findCutest(result));
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
這 16 行代碼只有一行是對我們有用(對于邏輯來說)的操作:
findCutest(result)
剩下的僅僅是開啟另外一個AsyncJob和傳遞結(jié)果與錯誤的樣板代碼绢片。此外滤馍,這些代碼并不用于特定的任務(wù),我們可以把其移動到其它地方而不影響編寫我們真正需要的業(yè)務(wù)代碼底循。
我們要怎么做呢巢株?
將方法findCutest分離出來,并和前面一樣返回臨時對象熙涤,供下步使用阁苞。
在 Java 中不能直接傳遞方法(函數(shù))所以我們需要通過類(和接口)來間接實現(xiàn)這樣的功能,我們來定義轉(zhuǎn)換方法的接口:
轉(zhuǎn)換方法的接口
/**
* 方法傳入接口
* T對應(yīng)于參數(shù)類型而R對應(yīng)于返回類型
* Created by philos on 17-9-16.
*/
public interface Func<T, R> {
R call(T t);
}
我們之前返回的臨時對象的start方法是通過回調(diào)返回的祠挫,但是現(xiàn)在是調(diào)用方法轉(zhuǎn)換需要返回臨時對象那槽,接著上面繼續(xù)看怎么改造。
我們新增一個方法等舔,就叫map骚灸,返回對象R,參考上面泛型方法慌植,AsyncJob代碼修改如下:
public abstract class AsyncJob<T> {
//包裝了方法甚牲,對外隱藏,外部只需傳入回調(diào)蝶柿,定義返回類型丈钙,調(diào)用是start即可
public abstract void start(Callback<T> callback);
//返回臨時對象 方法(需要傳入映射的方法Func<T, R> func攜帶的是對象R),
public <R> AsyncJob<R> map(final Func<T, R> func){
//使用自己交汤,為鏈?zhǔn)秸{(diào)用返回同樣的對象(return 一個新建的)
final AsyncJob<T> source = this;
return new AsyncJob<R>() {
@Override
public void start(final Callback<R> callback) {
source.start(new Callback<T>() {
@Override
public void onResult(T result) {
//方法 替換成func
//返回對象為R
R mapped = func.call(result);
callback.onResult(mapped);
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
}
}
前面map對應(yīng)的是分離一個方法返回的是實體對象的情況雏赦,那么如果是需要返回同樣的臨時對象的方法,比如上面的CatsHelper 的storedUriAsyncJob嵌套了兩個start方法芙扎,所以AsyncJob在添加一個方法星岗,就叫做flatMap
//返回臨時對象 方法(需要傳入映射的方法Func<T, AsyncJob<R>> func攜帶的是上一個臨時對象AsyncJob<R>)
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> callback) {
source.start(new Callback<T>() {
@Override
public void onResult(final T result) {
//傳入的方法不一樣,返回值不一樣纵顾,在這里有區(qū)別
AsyncJob<R> mapped = func.call(result);
mapped.start(new Callback<R>() {
@Override
public void onResult(R result) {
callback.onResult(result);
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
}
這里我們可以簡單的理解伍茄,我們之前把回調(diào)用start表示,但是對于start還調(diào)用了其他方法的施逾,還是存在嵌套敷矫,那么我們的map就是為了解決這個問題的例获。但是如果是多次start的嵌套,map明顯不滿足曹仗,這個時候我們使用flatMap 榨汤。
于是,我們的helper代碼修改如下:
public class CatsHelper {
private Cat findCutestCat(List<Cat> cats) {
return Collections.max(cats);
}
//監(jiān)聽接口
public interface CutestCatCallback {
void onCutestCatSaved(Uri uri);
void onQueryFailed(Exception e);
}
//映射
public AsyncJob<Uri> saveTheCutestCat4(String query){
//獲取貓列表 返回臨時對象 catsListAsyncJob
final AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper2.queryCats(query);
//查找最可愛貓 返回臨時對象 cutestCatAsyncJob
//這里用Func將方法findCutestCat(result)分離出來怎茫,并返回一個臨時對象cutestCatAsyncJob
final AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(new Func<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return findCutestCat(cats);
}
});
//保存貓到本地 返回臨時對象 storedUriAsyncJob
AsyncJob<AsyncJob<Uri>> storedUriAsyncJob = cutestCatAsyncJob.map(new Func<Cat, AsyncJob<Uri>>() {
@Override
public AsyncJob<Uri> call(Cat cat) {
return apiWrapper2.store(cat);
}
});
return storedUriAsyncJob;
}
}
RxJava
嘿收壕,你不需要把那些代碼拷到你的項目中,因為我們還是實現(xiàn)地不夠完全的轨蛤,僅僅算是非線程安全的 RxJava 的一小部分而已蜜宪。
它們之間只有一些差異:
AsyncJob<T>
就是實際上的 Observable
,它不僅可以只分發(fā)一個單一的結(jié)果也可以是一個序列(可以為空)祥山。
Callback<T>
就是 Observer
圃验,除了 Callback 少了onNext(T t)
方法。Observer 中在onError(Throwable t)
方法被調(diào)用后缝呕,會繼而調(diào)用onCompleted()
澳窑,然后 Observer 會包裝好并發(fā)送出事件流(因為它能發(fā)送一個序列)。
abstract void start(Callback<T> callback)
對應(yīng) Subscription subscribe(final Observer<? super T> observer)供常,這個方法也返回 Subscription 摊聋,在不需要它時你可以決定取消接收事件流。
除了map
和flatMap
方法栈暇,Observable
在 Observalbes 之上也有一些其它有用的操作麻裁。