自己動(dòng)手,鏈?zhǔn)骄幊?/h2>

參考# 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)行截圖:


image.png

小結(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é)果 :


image.png

可以看到,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)行


image.png

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容