Volley,Okhttp,Retrofit原始碼研究心得大總結
注:閱讀本篇博文之前建議閱讀上面幾個系列的博文,算是本篇文章的理論儲備。
以前在業餘的時候閱讀過Volley,Okhttp,Retrofit的原始碼,本篇就對他們做一個大總結和鞏固。
1、框架涉及到的設計模式 工廠模式:這個模式很簡單,與其說是一個模式,不如說它是一個變成習慣。該模式的直觀作用之一就是減少重複程式碼,方便複用。雖然該模式很簡單,但是各大框架裡面都有這個模式的影子,比如Gson,Okhttp,Retrofit,Glide等等,可謂使用廣泛。所以在平時開發中可以考慮使用此模式。
構建者模式:該模式簡單實用,可以說是一些公共框架的最愛,該模式主要是用來一步步構建所需物件的元件物件。當然該模式在可以用來優化方法引數過長的方法及其過載方法。
單例模式:如果說別的模式很陌生的話,那麼這個模式肯定不回陌生,不論在面試還是開發中都會遇到此模式。比如我們使用的Glide,EventBus等等一般都是用他們的單利物件。當然使用單利模式要留心一下記憶體洩露問題。
當然在框架中還可以看到其他常用的模式,比如介面卡模式和代理模式。在此不在贅述。
2、框架的必要性 其實框架的產生主要是為了解決特定的問題,比如圖片框架用來處理載入圖片、網路框架用來處理網路請求。這是框架層面的單一職責的表現。框架共同的特點就是程式碼複用,避免我們重複製造輪子。 如果沒有網路框架或者其他包含非同步任務的框架,那麼我們可能就要按照下圖的思路編造自己的輪子了(圖1:)
如上圖,如果你想重複造一個網路框架或者圖片框架的輪子,可能就逃不開上圖的思路,以網路框架為例: 1、構建一個網路請求任務(build tasks) 2、將任務新增到執行緒池中(enqueue) 3、執行緒池現成執行任務(execute tasks) 4、將非同步任務執行的結果通過回撥返回給客戶端( callback(result))
當然,可能你的非同步任務很簡單;但是設計一個非同步任務的工具基本上也就是下面的三種思路: 1、單個執行緒完成一個簡單的任務:直接new 一個thread 物件,在其run方法裡面執行 2、多個執行緒+佇列: 將任務新增到佇列中,然後開啟一個或者多個執行緒從任務佇列中獲取任務並執行,比如Volley就是此種設計 3、執行緒池的方式: 執行緒池的使用方式很簡單,因為Java封裝了執行緒池供我們使用,核心程式碼如下所示:
ExecutorService servcie = new ThreadPollExecutor(0,3,TimeUnit.SECONDS,new YourQueue<Runnablle>(),YouThreadFactory()); //執行非同步任務 servcie.enqueue(youTaskRunnable)
當然也會有其他不同的實現方式,水平有限不在列舉。
帶著上面的理論,開始下面的說明。
3、網路請求是什麼? 這個問題有點故弄玄虛,網路請求在本篇的觀點是客戶端發起請求(Request),然後伺服器返回資料(Response)的過程如圖所示(圖2)。
對該句話進行抽象的話可以剝離出三個介面:Request介面負責構建網路請求物件,Response介面負責封裝網路返回的資料。而執行網路請求到資料返回的可以抽象出另外一個介面Call,三者簡單的程式碼如下所示:
interface Request{
}
interface Response{
byte[] data;//伺服器資料
}
interface Call{
Response execute(Reqeust request);
}
上面的圖片和程式碼從抽象層面對此網路請求做了簡單的設計,在Java中發起網路請求的基本手段有Socket,HttpClient,HttpUrlConnection…在此基礎上還有一些各自封裝的框架,所以上面的圖如果考慮實現細節的話,就可以演變成下面的這幅圖(圖3):
不同的框架對上圖有不同的封裝和實現, 如果從設計模式的角度來看,其實就是一個策略模式,在執行網路請求的時候不同的框架都有自己的處理邏輯,但是最終返回的資料肯定是一樣的,比如你用Volley 請求一個美女圖片的資訊,換成Okhttp發起同樣的訪問時返回的不可能是動物的圖片。 所以用策略模式來拓展圖3的話,就可以演變成下面這幅圖(圖4):
4、各個框架的對圖1,圖4的演變
上面說過不同的網路框架對網路請求的封裝從整體上來看是策略模式的體現,那麼Volley對圖3或者說圖1做了怎樣的轉變呢?看下圖(圖5):
從上圖(圖5)看,volley採用的訪問網路的策略是使用HttpClient和HttpUrlConnection來進行網路請求,同時提供了不同的Request和Response實現。比如你想返回一個String,那麼就用StringRequest;需要返回Json的話就用JsonRequest。Volley內部幫我們自動呼叫parseNetwordResponse方法,該方法是Request基類的抽象方法:
protected abstract Response<T> parseNetwordResponse(NetworkResonse res);
然後上圖中不同的Request物件實現parseNetwordResponse方法,這其實就是模板模式的簡單應用。
根據圖1的理論,來簡單看下Volley是怎麼處理非同步請求的。在volley中是沒有用到執行緒池的,其內部也就預設初始化了四個NetworkDispatcher物件(thread)。然後將所有的請求新增到BlockingQueue中。工作時每個NetworkDispatcher不斷從佇列中獲取Request並執行的過程:
public void start() {
for (int i = 0; i < mDispatchers.length; i++) {//預設大小是4
NetworkDispatcher networkDispatcher =
new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
而NetworkDispathcer的run方法大致如下所示:
public void run() {
while (true) {//一個while迴圈
Request<?> request = mQueue.take();
NetworkResponse networkResponse = mNetwork.performRequest(request);
//在工作執行緒將資料轉換成Response物件
Response<?> response =request.parseNetworkResponse(networkResponse);
//callback返回資料
mDelivery.postResponse(request, response);
}
}
說完了Volley,那麼在來看看Okhttp對圖3做了怎樣的演化。Okhttp是以一些列攔截器為核心來執行網路請求的,該框架對圖3的演化可以用下圖(圖6)來表示: 關於Okhttp攔截其的工作細節,可以點此瞭解更多。 從圖5和圖6來看,同樣是執行網路請求,二者的實現差異還是巨大的。都是執行網路請求,不同的實現有不同的策略。算是策略模式的一種體現吧。
最後在簡單看一下Okhttp是怎麼執行同步和非同步請求的,為了達到這一目的,Okhttp設計了一個介面:
interface Call {
Response execute() throws IOException;
//Callback 為非同步請求回撥
void enqueue(Callback responseCallback);
}
Okhttp的通過和非同步請求程式碼如下:
Call call = new OkhttClient().newCall(request);
Response response = call.execute();
//非同步請求
Response response = okhttpClient.newCall(request).enqueue(callback);
其實在Java中任何非同步任務的處理特別是牽扯到執行緒池的時候,基本都是交給Thread或者Runnable來完成的。Okhttp也不例外。Okhttp的同步請求直接呼叫RealCall,而非同步請求則交給了AsyncCall(extends NamedRunnable)。具體執行非同步請求的地方,可以參考Okhttp的Dispatcher,或者點選此處瞭解更多