《NotRxJava guide for lazy folks》笔记,揭开RxJava框架原理
原文及译文
这篇文章是我目前看过的对RxJava设计原理讲解的最通俗易懂的
原文:NotRxJava guide for lazy folks
译文:NotRxJava懒人专用指南-RxJava的演进过程
需求
- 获取一个猫的列表
- 选出最可爱的那只猫
- 最后将这个最可爱的猫保存数据库
Cat类和API
public class Cat implements Comparable<Cat>{
Bitmap image;
int cuteness;
@Override
public int compareTo(Cat another) {
return Integer.compare(cuteness, another.cuteness);
}
}
步骤二选出最可爱的猫是同步的过程,并不耗时,所以未定义接口
public interface Api {
List<Cat> queryCats(String query);
Uri store(Cat cat);
}
同步的代码实现
public class CatsHelper {
Api api;
public Uri saveTheCutestCat(String query){
try{
// 获取一个猫的列表
List<Cat> cats = api.queryCats(query);
// 选出最可爱的那只猫
Cat cutest = findCutest(cats);
// 最后将这个最可爱的猫保存数据库
Uri savedUri = api.store(cutest);
return savedUri;
} catch (Exception e) { // 错误统一处理
e.printStackTrace()
return someDefaultValue;
}
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
异步代码实现
首先改变下接口的定义,处理结果由原来的直接返回变成通过回调接收
public interface Api {
void queryCats(String query, CatsQueryCallback catsQueryCallback);
void store(Cat cat, StoreCallback storeCallback);
interface CatsQueryCallback {
void onCatListReceived(List<Cat> cats);
void onQueryFailed(Exception e);
}
interface StoreCallback{
void onCatStored(Uri uri);
void onStoreFailed(Exception e);
}
}
这层层的缩进,回调中的回调,就是所谓的回调地狱
public class CatsHelper {
Api api;
public void saveTheCutestCat(String query, CutestCatCallback cutestCatCallback) {
// 获取一个猫的列表
api.queryCats(query, new Api.CatsQueryCallback() {
@Override
public void onCatListReceived(List<Cat> cats) {
// 选出最可爱的那只猫
Cat cutest = findCutest(cats);
// 最后将这个最可爱的猫保存数据库
api.store(cutest, new Api.StoreCallback() {
@Override
public void onCatStored(Uri uri) {
cutestCatCallback.onCutestCatSaved(uri);
}
@Override
public void onStoreFailed(Exception e) {
cutestCatCallback.onError(e);
}
});
}
@Override
public void onQueryFailed(Exception e) {
cutestCatCallback.onError(e);
}
});
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
public interface CutestCatCallback {
void onCutestCatSaved(Uri uri);
void onError(Exception e);
}
}
泛型回调
分析我们之前定义的三个回调函数CatsQueryCallback 、StoreCallback、CutestCatCallback,可以发现里面都定义了两个方法
- 一个接收处理结果的方法,只是接收的结果类型不一样
- 一个接收处理过程中异常的方法,接收的内容都是Exception或其子类
可以定义一个通用的泛型回调函数
public interface Callback<T> {
void onResult(T result);
void onError(Exception e);
}
然后再新增一个ApiWrapper类
这一步我一开始比较不能理解,为什么要在CatsQueryCallback和StoreCallback回调里再回调Callback呢?为什么不直接修改Api接口,讲queryCats()和store()方法的回调类型都设置成Callback类型呢?
这样子改当然可以,但是前提是这个Api是你自己写的,如果这个Api是在别人的jar包里提供的呢?即使是自己写的Api也不建议修改,最好的方法就是像这样再包装一层。一个好的框架就是要通用,不能说我用你这个框架还用修改自己的Api设计,这样就不合理了
包装一层将queryCats()和store()的回调都转成CallBack类型
public class ApiWrapper {
Api api;
public void queryCats(String query, Callback<List<Cat>> catsCallback){
api.queryCats(query, new Api.CatsQueryCallback() {
@Override
public void onCatListReceived(List<Cat> cats) {
catsCallback.onResult(cats);
}
@Override
public void onQueryFailed(Exception e) {
catsCallback.onError(e);
}
});
}
public void store(Cat cat, 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);
}
});
}
}
然后再修改CatsHelper类,这里saveTheCutestCat()的回调类型也可以改成Callback类型了。有人会问,为什么上面Api的回调采用转发而不是改成Callback类型,而这里却可以修改类型。因为上面是Api,一旦设计好不建议再修改,而CatsHelper是业务相关的类,可以修改其实现代码。
由于ApiWrapper.queryCats()、ApiWrapper.store()和CatsHelper.saveTheCutestCat()统一了回调函数的类型,所以CatsHelper.saveTheCutestCat()方法的回调直接传给了最里层的ApiWrapper.store(),这样就少了一层回调,代码也简洁了不少
public class CatsHelper{
ApiWrapper apiWrapper;
public void saveTheCutestCat(String query, Callback<Uri> cutestCatCallback){
apiWrapper.queryCats(query, new Callback<List<Cat>>() {
@Override
public void onResult(List<Cat> cats) {
Cat cutest = findCutest(cats);
apiWrapper.store(cutest, cutestCatCallback);
}
@Override
public void onError(Exception e) {
cutestCatCallback.onError(e);
}
});
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
异步任务类AsyncJob
分析上面的queryCats()、store()和saveTheCutestCat()这些异步方法,我们可以抽象出一个异步任务类,暂且就叫做AsyncJob。
定义如下,接口里有个start方法,在里面可以具体实现任务内容,该方法接收Callback回调
public abstract class AsyncJob<T> {
public abstract void start(Callback<T> callback);
}
然后修改ApiWrapper包装类里的queryCats()和store()方法,各返回一个具体的AsyncJob实现。
需要注意的是这两个方法的逻辑变了,之前的实现是执行异步任务,现在是返回异步任务AsyncJob,但是并未执行异步任务。异步任务被放在了start方法里(可以发现start方法参数和内容和原来的实现一模一样),需要调用返回的AsyncJob的start方法才会开始执行异步任务
public class ApiWrapper {
Api api;
public AsyncJob<List<Cat>> queryCats(String query) {
return new AsyncJob<List<Cat>>() {
@Override
public void start(Callback<List<Cat>> catsCallback) {
api.queryCats(query, new Api.CatsQueryCallback() {
@Override
public void onCatListReceived(List<Cat> cats) {
catsCallback.onResult(cats);
}
@Override
public void onQueryFailed(Exception e) {
catsCallback.onError(e);
}
});
}
};
}
public AsyncJob<Uri> store(Cat cat) {
return new AsyncJob<Uri>() {
@Override
public void start(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);
}
});
}
};
}
}
然后再修改CatsHelper.saveTheCutestCat()方法,也是改成返回一个AsyncJob。在start实现里是执行queryCats返回的AsyncJob异步任务,成功后再调用store返回的AsyncJob。正如我上面所说的,要调用AsyncJob.start方法才开启异步任务。
public class CatsHelper {
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
return new AsyncJob<Uri>() {
@Override
public void start(Callback<Uri> cutestCatCallback) {
apiWrapper.queryCats(query)
.start(new Callback<List<Cat>>() {
@Override
public void onResult(List<Cat> cats) {
Cat cutest = findCutest(cats);
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);
}
});
}
};
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
我相信很多人看了上面修改后的CatsHelper类又要开始吐槽了,怎么又改复杂了,看看这迷之缩进。
引进这AsyncJob到底有什么好处呢?别急,好戏才刚要开始。
Map实现
我们修改下CatsHelper,降获取最可爱的猫这个过程也改成一个AsyncJob任务cutestCatAsyncJob,其start实现是先执行catsListAsyncJob任务,然后在成功的回调里再执行findCutest()方法。
然后storedUriAsyncJob的start方法里在来执行这个cutestCatAsyncJob
修改完虽然没有简单多少,但是逻辑清晰许多,仔细看catsListAsyncJob、cutestCatAsyncJob、storedUriAsyncJob这三个任务一个个列下来,是不是有点最开始的同步代码的实现的那种感觉,逻辑也比较清晰。
cutestCatAsyncJob任务里包含catsListAsyncJob
而storedUriAsyncJob包含cutestCatAsyncJob(即包含catsListAsyncJob和cutestCatAsyncJob这两个任务)
public class CatsHelper {
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
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) {
Cat cutest = findCutest(cats);
callback.onResult(cutest);
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
AsyncJob<Uri> storedUriAsyncJob = new AsyncJob<Uri>() {
@Override
public void start(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;
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
这里先重点看下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) {
Cat cutest = findCutest(cats);
callback.onResult(cutest);
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
我们先把他放进一个叫做createCutestCatAsyncJob()的方法里再来做说明,看下这个方法能不能抽象出一个通用的方法。
createCutestCatAsyncJob方法是把一个AsyncJob(获取猫列表)转化成另一个AsyncJob(获取最可爱的猫)。
其中和转化业务有关的关键代码是Cat cutest = findCutest(cats)这句话,把List<Cat>转成Cat,我们可以把这句话用一个转化接口抽出来,其余的代码成为通用的模板
private void createCutestCatAsyncJob(AsyncJob<List<Cat>> catsListAsyncJob) {
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) {
Cat cutest = findCutest(cats);
callback.onResult(cutest);
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
return cutestCatAsyncJob;
}
Func这个接口定义了一个方法call,作用是吧参数T类型转化成返回值R类型,具体转化由用户自己实现
public interface Func<T, R> {
R call(T t);
}
利用Func接口,我们定义一个通用的转化方法,就叫做Map吧,和RxJava里的一样,定义如下
public <R> AsyncJob<R> map(AsyncJob<T> source, Func<T, R> func){
return new AsyncJob<R>() {
@Override
public void start(Callback<R> callback) {
source.start(new Callback<T>() {
@Override
public void onResult(T result) {
R mapped = func.call(result);
callback.onResult(mapped);
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
}
再把这个方法放在AsyncJob里面,这样AsyncJob对象就可以直接调用map方法,映射成另一个AsyncJob了,修改后的AsyncJob如下所示
public abstract class AsyncJob<T> {
public abstract void start(Callback<T> callback);
public <R> AsyncJob<R> map(Func<T, R> func){
final AsyncJob<T> source = this;
return new AsyncJob<R>() {
@Override
public void start(Callback<R> callback) {
source.start(new Callback<T>() {
@Override
public void onResult(T result) {
R mapped = func.call(result);
callback.onResult(mapped);
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
}
}
利用map方法,CatsHelper修改如下
public class CatsHelper {
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(new Func<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return findCutest(cats);
}
});
AsyncJob<Uri> storedUriAsyncJob = new AsyncJob<Uri>() {
@Override
public void start(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;
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
FlatMap实现
既然map这么好用我们试着把下面这段的代码也有话一下吧,这一坨代码看着实在是不舒服
试着将cutestCatAsyncJob转成storedUriAsyncJob
AsyncJob<Uri> storedUriAsyncJob = new AsyncJob<Uri>() {
@Override
public void start(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);
}
});
}
};
修改后的CatsHelper代码如下
public class CatsHelper {
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(new Func<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return findCutest(cats);
}
});
AsyncJob<AsyncJob<Uri>> storedUriAsyncJob = cutestCatAsyncJob.map(new Func<Cat, AsyncJob<Uri>>() {
@Override
public AsyncJob<Uri> call(Cat cat) {
return apiWrapper.store(cat);
}
});
return storedUriAsyncJob;
//^^^^^^^^^^^^^^^^^^^^^^^ will not compile
// Incompatible types:
// Required: AsyncJob<Uri>
// Found: AsyncJob<AsyncJob<Uri>>
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
可是报错了,没事,返回值比匹配那就该一下,改成AsyncJob<Uri>不久得了,如下所示
public class CatsHelper {
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(new Func<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return findCutest(cats);
}
});
AsyncJob<AsyncJob<Uri>> storedUriAsyncJob = cutestCatAsyncJob.map(new Func<Cat, AsyncJob<Uri>>() {
@Override
public AsyncJob<Uri> call(Cat cat) {
return apiWrapper.store(cat);
}
});
return storedUriAsyncJob;
//^^^^^^^^^^^^^^^^^^^^^^^ will not compile
// Incompatible types:
// Required: AsyncJob<Uri>
// Found: AsyncJob<AsyncJob<Uri>>
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
还是报错,saveTheCutestCat要的返回值是AsyncJob<Uri>,而不是AsyncJob<AsyncJob<Uri>>
AsyncJob<AsyncJob<Uri>>变成两个异步任务嵌套了
这是因为findCutest(cats)是同步代码,方法执行完可以直接得到处理完的结果
而apiWrapper.store()是异步任务,要等待执行完才能够得到处理的结果
既然这样的话何不apiWrapper.store().start()开启异步任务,等任务执行完再返回结果,这样的话map就不适用了,我们再定义一个叫做flatMap的方法,如下所示
可以看到flatMap的参数Func<T, AsyncJob<R>>,第二个泛型是个AsyncJob异步任务,所以Func.call()方法执行完返回的是一个AsyncJob异步任务,接下来再直接运行该异步,等该任务运行完再回调
public abstract class AsyncJob<T> {
public abstract void start(Callback<T> callback);
public <R> AsyncJob<R> map(Func<T, R> func){
final AsyncJob<T> source = this;
return new AsyncJob<R>() {
@Override
public void start(Callback<R> callback) {
source.start(new Callback<T>() {
@Override
public void onResult(T result) {
R mapped = func.call(result);
callback.onResult(mapped);
}
@Override
public void onError(Exception e) {
callback.onError(e);
}
});
}
};
}
public <R> AsyncJob<R> flatMap(Func<T, AsyncJob<R>> func){
final AsyncJob<T> source = this;
return new AsyncJob<R>() {
@Override
public void start(Callback<R> callback) {
source.start(new Callback<T>() {
@Override
public void onResult(T result) {
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);
}
});
}
};
}
}
最后利用flatMap,CatsHelper修改后的代码如下,是不是一下子清爽了许多,是不是越来越像一开始的同步代码实现的三个步骤,可怕的回调地狱嵌套也减少到只有一层回调
public class CatsHelper {
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
// 获取一个猫的列表
AsyncJob<List<Cat>> catsListAsyncJob = apiWrapper.queryCats(query);
// 选出最可爱的那只猫
AsyncJob<Cat> cutestCatAsyncJob = catsListAsyncJob.map(new Func<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return findCutest(cats);
}
});
// 最后将这个最可爱的猫保存数据库
AsyncJob<Uri> storedUriAsyncJob = cutestCatAsyncJob.flatMap(new Func<Cat, AsyncJob<Uri>>() {
@Override
public AsyncJob<Uri> call(Cat cat) {
return apiWrapper.store(cat);
}
});
return storedUriAsyncJob;
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
还可以改成如下的链式调用,是不是感觉很熟悉,和RxJava的使用一模一样
public class CatsHelper {
ApiWrapper apiWrapper;
public AsyncJob<Uri> saveTheCutestCat(String query) {
AsyncJob<Uri> storedUriAsyncJob = apiWrapper.queryCats(query)
.map(new Func<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return findCutest(cats);
}
})
.flatMap(new Func<Cat, AsyncJob<Uri>>() {
@Override
public AsyncJob<Uri> call(Cat cat) {
return apiWrapper.store(cat);
}
});
return storedUriAsyncJob;
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}
当然我们这个例子只是为了让大家好理解RxJava的实现原理,以下是我们实现的类和RxJava中对应的类
- AsyncJob对应Observable
- Callback对应Observer
- AsyncJob.start()方法对应Observable.subscribe方法
- 出了map和flatMap外,RxJava中还定义了很多功能丰富的操作符
下面是用RxJava实现的代码
public class ApiWrapper {
Api api;
public Observable<List<Cat>> queryCats(final String query) {
return Observable.create(new Observable.OnSubscribe<List<Cat>>() {
@Override
public void call(final Subscriber<? super List<Cat>> subscriber) {
api.queryCats(query, new Api.CatsQueryCallback() {
@Override
public void onCatListReceived(List<Cat> cats) {
subscriber.onNext(cats);
}
@Override
public void onQueryFailed(Exception e) {
subscriber.onError(e);
}
});
}
});
}
public Observable<Uri> store(final Cat cat) {
return Observable.create(new Observable.OnSubscribe<Uri>() {
@Override
public void call(final Subscriber<? super Uri> subscriber) {
api.store(cat, new Api.StoreCallback() {
@Override
public void onCatStored(Uri uri) {
subscriber.onNext(uri);
}
@Override
public void onStoreFailed(Exception e) {
subscriber.onError(e);
}
});
}
});
}
}
public class CatsHelper {
ApiWrapper apiWrapper;
public Observable<Uri> saveTheCutestCat(String query) {
Observable<List<Cat>> catsListObservable = apiWrapper.queryCats(query);
Observable<Cat> cutestCatObservable = catsListObservable.map(new Func1<List<Cat>, Cat>() {
@Override
public Cat call(List<Cat> cats) {
return CatsHelper.this.findCutest(cats);
}
});
Observable<Uri> storedUriObservable = cutestCatObservable.flatMap(new Func1<Cat, Observable<? extends Uri>>() {
@Override
public Observable<? extends Uri> call(Cat cat) {
return apiWrapper.store(cat);
}
});
return storedUriObservable;
}
private Cat findCutest(List<Cat> cats) {
return Collections.max(cats);
}
}