參考# Bolts-Android想深入了解的同學(xué)可以直接下載下來研究。
舉個(gè)粟子
在實(shí)際的開發(fā)過程中,可能會(huì)遇到這樣的情況,頁面的展示需要一些數(shù)據(jù),這些數(shù)據(jù)需要調(diào)用不同的接口A、B、C、來獲取,且必須按有順序調(diào)用,因?yàn)锽接口需要A接口的返回?cái)?shù)據(jù) ,C接口需要B接口的返回?cái)?shù)據(jù)。
這個(gè)時(shí)候,你的代碼可能是這樣的:
private void fetchData() {
callA("a");
}
private void callA(String param) {
//fetch A data, then call B
callB("b");
}
private void callB(String param) {
//fetch B data, then call C
callC("c");
}
private void callC(String param) {
//fetch c data
//then display
}
這樣寫邏輯上沒有問題,功能上也沒有問題。但是看代碼的時(shí)候就有點(diǎn)難受了,很難從宏觀上理解,做這個(gè)功能,怎樣的一個(gè)流程,只能點(diǎn)進(jìn)各個(gè)方法里去看,然后跳轉(zhuǎn),再跳轉(zhuǎn)......
當(dāng)你理解我上面在說什么時(shí)候,你肯定會(huì)想辦法,讓代碼看起來更清晰明了,可能你會(huì)有這樣的想法:
public interface IResult<T> {
void onResult(T data);
}
private void fetchData() {
callA("a", new IResult<String>() {
@Override
public void onResult(String data) {
callB(data, new IResult<String>() {
@Override
public void onResult(String data) {
callC(data, new IResult<String>() {
@Override
public void onResult(String data) {
//then display
}
});
}
});
}
});
}
private void callA(String param, IResult<String> callback) {
//fetch A data, then callback
callback.onResult("b");
}
private void callB(String param, IResult<String> callback) {
//fetch B data, then callback
callback.onResult("c");
}
private void callC(String param, IResult<String> callback) {
//fetch C data, then callback
callback.onResult("success");
}
這樣寫之后,就可以在
fetchData()里清楚明白的知道,需要調(diào)用接口A、B、C之后,再進(jìn)行展示的流程。但是多層的嵌套回調(diào),雖然流程在一個(gè)方法里,但是看起來還是有點(diǎn)費(fèi)勁??梢栽僮饕稽c(diǎn)優(yōu)化:
private void fetchData() {
final IResult<String> cCallback = new IResult<String>() {
@Override
public void onResult(String data) {
//then display
}
};
final IResult<String> bCallback = new IResult<String>() {
@Override
public void onResult(String data) {
callC(data, cCallback);
}
};
IResult<String> aCallback = new IResult<String>() {
@Override
public void onResult(String data) {
callB(data, bCallback);
}
};
callA("a", aCallback);
}
看到這里,你可能會(huì)想,上面寫的一堆跟我最開頭參考的東西,有什么關(guān)系?
確實(shí)也沒什么關(guān)系。
不過,我想通過上面的粟子,來引出鏈?zhǔn)骄幊獭?/p>
試想,如果我們的代碼可以像我們的語言表達(dá)那樣,先這樣,再這樣,再這樣,最后這樣,這樣寫代碼的話,是不是會(huì)更容易理解一些?下面就是一個(gè)鏈?zhǔn)降呐e例:
Task.call(new Callable<String>() {
@Override
public String call() {
return callA("a");
}
}).continueWith(new Continuation<String, String>() {
@Override
public String then(Task<String> task) {
return callB(task.getResult());
}
}).continueWith(new Continuation<String, String>() {
@Override
public String then(Task<String> task) {
return callC(task.getResult());
}
}).continueWith(new Continuation<String, Void>() {
@Override
public Void then(Task<String> task) {
//display
return null;
}
});
根據(jù)上面的鏈?zhǔn)嚼樱覀兎治鲆幌?,想要進(jìn)行鏈?zhǔn)骄幊?,要編寫一個(gè)處理各位任務(wù)的類
Task,其中有一個(gè)靜態(tài)方法call(),這個(gè)方法需要傳入一個(gè)Callable參數(shù),返回一個(gè)Task實(shí)例,方便鏈?zhǔn)降恼{(diào)用其他方法continueWith(),這個(gè)continueWith方法需要傳入一個(gè)Continuation的實(shí)例,返回一個(gè)Task實(shí)例。
了解之后,那么我們來自己寫一個(gè)的簡(jiǎn)單的鏈?zhǔn)教幚眍?/h3>
定義一個(gè)處理類MyTask,寫兩個(gè)方法
public class MyTask<T> {
private T result;
public static <TResult> MyTask<TResult> doSth(Callable<TResult> callable) {
return new MyTask<>();
}
public <TContinueResult> MyTask<TContinueResult> continueDoSth(MyContinuation<T, TContinueResult> continuation) {
return new MyTask<>();
}
public T getResult() {
return result;
}
}
可以看到上面的方法里直接
new Task<>()返回了,因?yàn)椴恢碧幚?,想先看看效果?/p>
MyTask.doSth(new Callable<String>() {
@Override
public String call() {
return callA("a");
}
}).continueDoSth(new MyContinuation<String, String>() {
@Override
public void then(MyTask<String> task) {
callB(task.getResult());
}
}).continueDoSth(new MyContinuation<String, String>() {
@Override
public void then(MyTask<String> task) {
callC(task.getResult());
}
}).continueDoSth(new MyContinuation<String, String>() {
@Override
public void then(MyTask<String> task) {
//display
}
});
MyContinuation.java
public interface MyContinuation<TTaskResult, TContinuationResult> {
TContinuationResult then(MyTask<TTaskResult> task);
}
現(xiàn)在,在具體使用時(shí)的寫法上,已經(jīng)跟 Bolts-Android里的一部分差不多了,就是在具體的處理類的實(shí)現(xiàn)上并沒有進(jìn)行實(shí)現(xiàn)。
MyTask .doSth方法,需要執(zhí)行傳入的Callable并把Callable的執(zhí)行結(jié)果寫到一個(gè)實(shí)例化的MyTask里。那么現(xiàn)在我們改造一下MyTask類里的doSth方法。
public class MyTask<T> {
private T result;
public static <TResult> MyTask<TResult> doSth(Callable<TResult> callable) {
MyTask<TResult> task = new MyTask<>();
try {
task.result = callable.call();
} catch (Exception e) {
e.printStackTrace();
task.result = null;
}
return task;
}
public <TContinueResult> MyTask<TContinueResult> continueDoSth(MyContinuation<T, TContinueResult> continuation) {
MyTask<TContinueResult> task = new MyTask<>();
task.result = continuation.then(this);
return task;
}
public T getResult() {
return result;
}
}
最后完整的代碼
MyTask.java
public class MyTask<T> {
private T result;
public static <TResult> MyTask<TResult> doSth(Callable<TResult> callable) {
MyTask<TResult> task = new MyTask<>();
try {
task.result = callable.call();
} catch (Exception e) {
e.printStackTrace();
task.result = null;
}
return task;
}
public <TContinueResult> MyTask<TContinueResult> continueDoSth(MyContinuation<T, TContinueResult> continuation) {
MyTask<TContinueResult> task = new MyTask<>();
task.result = continuation.then(this);
return task;
}
public T getResult() {
return result;
}
}
MyContinuation.java
public interface MyContinuation<TTaskResult, TContinuationResult> {
TContinuationResult then(MyTask<TTaskResult> task);
}
Test.java
public class Test {
private static final String TAG = "------------------------> ";
public void fetchData() {
MyTask.doSth(new Callable<String>() {
@Override
public String call() {
return callA("a");
}
}).continueDoSth(new MyContinuation<String, String>() {
@Override
public String then(MyTask<String> task) {
return callB(task.getResult());
}
}).continueDoSth(new MyContinuation<String, String>() {
@Override
public String then(MyTask<String> task) {
return callC(task.getResult());
}
}).continueDoSth(new MyContinuation<String, String>() {
@Override
public String then(MyTask<String> task) {
//display
System.out.println(task.getResult());
return null;
}
});
}
private String callA(String param) {
//fetch A data, then callback
System.out.println("callA" + TAG + param);
return param + "b";
}
private String callB(String param) {
//fetch B data, then callback
System.out.println("callB" + TAG + param);
return param + "c";
}
private String callC(String param) {
//fetch C data, then callback
System.out.println("callC" + TAG + param);
return param + "display";
}
}
運(yùn)行截圖:

小結(jié)
到這里,算是打開了鏈接編程的門了。不過,你可能會(huì)發(fā)現(xiàn),根據(jù)上面寫的代碼,只能處理單線程的鏈接編程,至于真正的請(qǐng)求接口,肯定是要開個(gè)線程異步請(qǐng)求,而最后的展示又是要在UI線程執(zhí)行的,所以還要涉及到線程相關(guān)的知識(shí),有興趣的同學(xué)自行研究。
補(bǔ)充多線程鏈?zhǔn)骄幊痰拇a
就拿異步請(qǐng)求接口的情況舉例吧。
我們期望在編寫代碼邏輯的時(shí)候,也可以像下面一樣,鏈?zhǔn)骄幊蹋?/p>
public void fetchData(final String param) {
MyTask.callInBackground(new Callable<String>() {
@Override
public String call() throws Exception {
return request(param);
}
}).continueWith(new MyContinuation<String, String>() {
@Override
public String then(MyTask<String> task) {
display(task.getResult());
return null;
}
}, MyTask.uiExecutor);
}
private String request(String param) throws InterruptedException {
L.info("request start...");
Thread.sleep(5000);
L.info("request complete...");
return "\n" + "request param: " + param + "\n"
+ "response data: " + "this is returned json data";
}
private void display(String content) {
L.info("display start...");
L.info("content: " + content);
}
因?yàn)榻涌谡?qǐng)求這種耗時(shí)操作不能在主線程里做,所以調(diào)用接口需要在異步線程里做,所以需要在MyTask類里定義一個(gè)方法public static <TResult> MyTask<TResult> callInBackground(final Callable<TResult> callable)
為了讓這個(gè)方法在異步線程里執(zhí)行,并且執(zhí)行完之后下一步能在主線程里更新UI,我們還需要得到主線程。方便起見,我們?cè)贛yTask類里分別定義出一個(gè)異步線程和一個(gè)UI線程:
public static Executor backgroundExecutor = Executors.newFixedThreadPool(5);
public static Executor uiExecutor = new UiExecutor();
UIExecutor.java
public class UiExecutor implements Executor {
@Override
public void execute(Runnable command) {
new Handler(Looper.getMainLooper()).post(command);
}
}
接著編寫callInBackground方法和continueWith方法
public static <TResult> MyTask<TResult> callInBackground(final Callable<TResult> callable) {
final MyTask<TResult> task = new MyTask<>();
backgroundExecutor.execute(new Runnable() {
@Override
public void run() {
L.info("callInBackground run...");
try {
task.result = callable.call();
} catch (Exception e) {
e.printStackTrace();
L.error("callInBackground exception: " + e.getMessage());
}
L.info("callInBackground complete...");
}
});
return task;
}
public <TContinueResult> MyTask<TContinueResult> continueWith(final MyContinuation<T, TContinueResult> continuation, Executor executor) {
final MyTask<TContinueResult> task = new MyTask<>();
executor.execute(new Runnable() {
@Override
public void run() {
L.info("continueWith run...");
task.result = continuation.then(thisTask);
L.info("continueWith complete...");
}
});
return task;
}
運(yùn)行一下,看到結(jié)果 :

可以看到,continueWith里面拿到的請(qǐng)求數(shù)據(jù)為空??墒俏覀兠髅髟?code>request(String param)方法里返回了數(shù)據(jù)的呀,為什么拿不到呢?
其實(shí)這就是因?yàn)楫惒秸?qǐng)求接口的時(shí)候,請(qǐng)求和返回都是有耗時(shí)操作,如果沒有相應(yīng)的同步機(jī)制,那么在UI線程的代碼是不會(huì)等異步線程的請(qǐng)求結(jié)果回來之后再執(zhí)行的。
所以,在這里需要加個(gè)同步機(jī)制,在callInBackground方法里的異步操作沒執(zhí)行完之前,不執(zhí)行continueWith方法。這里就需要用到synchronized關(guān)鍵字了。
修改MyTask類,給其添加一下Object鎖
private static final Object lock = new Object();
再修改callInBackground方法和continueWith方法
public static <TResult> MyTask<TResult> callInBackground(final Callable<TResult> callable) {
final MyTask<TResult> task = new MyTask<>();
backgroundExecutor.execute(new Runnable() {
@Override
public void run() {
L.info("callInBackground run...");
try {
synchronized (lock) {
task.result = callable.call();
}
} catch (Exception e) {
e.printStackTrace();
L.error("callInBackground exception: " + e.getMessage());
}
L.info("callInBackground complete...");
}
});
return task;
}
public <TContinueResult> MyTask<TContinueResult> continueWith(final MyContinuation<T, TContinueResult> continuation, Executor executor) {
final MyTask<TContinueResult> task = new MyTask<>();
executor.execute(new Runnable() {
@Override
public void run() {
synchronized (lock) {
L.info("continueWith run...");
task.result = continuation.then(thisTask);
L.info("continueWith complete...");
}
}
});
return task;
}
其實(shí)只是在
callInBackground方法里執(zhí)行task.result = callable.call();時(shí)給其加一個(gè)鎖,并且在continueWith方法執(zhí)行task.result = continuation.then(thisTask);時(shí)為其加鎖。
在這種情況下,代碼執(zhí)行到task.result = continuation.then(thisTask);時(shí)就會(huì)等task.result = callable.call();執(zhí)行完釋放鎖之后才會(huì)執(zhí)行。
再次運(yùn)行

可以看到,開始請(qǐng)求之后,過了5秒,請(qǐng)求完成,才開始執(zhí)行continueWith run里的面的display,展示了請(qǐng)求拿到的數(shù)據(jù)。流程符合我們的預(yù)期,完成!