一、Okhttp3
okhttp由square公司出品,目前主流的網(wǎng)絡(luò)庫,功能相對比較全面。
1.1總體框架設(shè)計

Okhttp 3.4之前有個類:HttpEngine,網(wǎng)絡(luò)請求和響應(yīng)的核心邏輯封裝在HttpEngine中,3.4及其以后版本重構(gòu)了,去掉了這個類,它的功能在getResponseWithInterceptorChain中拆分為CacheInterceptor、ConnectInterceptor、CallServerInterceptor 等幾個攔截器來實現(xiàn)。
1.2請求流程

1.3 類圖

1.4 異步請求時序圖

基本流程:
1)Okhttp通過newCall創(chuàng)建RealCall發(fā)起一個網(wǎng)絡(luò)請求任務(wù),同時傳入Request。
2)網(wǎng)絡(luò)請求的調(diào)度交給Dispatcher通過一個受限制的Cached類型的線程池來處理。
3)3.4之前通過HttpEngine來處理網(wǎng)絡(luò)請求,之后通過對應(yīng)的幾個固定攔截器來處理網(wǎng)絡(luò)請求。
4)callback返回Response。
二、Volley
Volley 是 Google 1/0 2013 發(fā)布的Android異步網(wǎng)絡(luò)請求框架和圖片加載框架。適合數(shù)據(jù)量小,通信頻繁的網(wǎng)絡(luò)操作。
2.1總體框架設(shè)計

上層接受自定義Reuqest,請求放入RequestQueue中,通過兩種Dispatch Thread不斷從RequestQueue中取出請求,根據(jù)是否已緩存調(diào)用Cache或Network這兩類數(shù)據(jù)獲取接口之一,
從內(nèi)存緩存或是服務(wù)器取得請求的數(shù)據(jù),然后交由ResponseDelivery去做結(jié)果分發(fā)及回調(diào)處理。
2.2請求流程

2.3 類圖

2.4 請求時序圖

基本流程:
1)Volley創(chuàng)建一個RequestQueue,該Queue包含1個CacheDispatcher線程,4個NetworkDispatcher線程。
2)request設(shè)置能緩存則請求放入mCacheQueue,交由CacheDispatcher處理,獲取緩存。不能緩存放入mNetworkQueue,走網(wǎng)絡(luò)請求。
3)真正網(wǎng)絡(luò)請求由BasicNetwork.performRequest執(zhí)行。
4)最終解析response并返回callback。
三、Okhttp3 和 Volley幾個點的對比
3.1 框架設(shè)計
這里只是對框架設(shè)計本身做對比,而非功能層面。
Okhttp3
優(yōu)點:
- 首先設(shè)計上一個最大的直觀感受是無處不在的建造者模式。不僅包括OkhttpClient、Request、Response這種核心組件,也包括CacheControl、Headers這種具體功能都使用的建造者模式,配置非常靈活。
- 通過各種Interceptor以責(zé)任鏈模式對Request和Response進(jìn)行監(jiān)聽、參數(shù)獲取或者調(diào)整某些參數(shù)信息。
缺點:
- Request個性配置支持不夠,例如:為不同Request設(shè)置不同的超時時間,因為超時是由OkHttpClient統(tǒng)一設(shè)置的,當(dāng)然也可以對個性化定制的Request去走OkHttpClient.newBuilder(),但是這里考慮到并發(fā)問題,并不是一個非常好的策略。
- Request中的配置信息基本上異常都是throw一個Exception,這也就要求初始化Request的時候必須做好try catch,不然線上環(huán)境很容易crash,例如:.url()設(shè)置請求url,如果url格式出現(xiàn)問題,會在HttpUrl.parse里直接throw異常,交給調(diào)用處去處理,這時候如果調(diào)用處疏忽沒做處理,就會crash。
- callBack返回的異常情況需要自行封裝,嚴(yán)格說也不能叫做是缺點吧,只是這點Volley做得更好。
Volley
優(yōu)點:
- Request拓展性非常強。
-
callBack異常有封裝VolleyError,同時也封裝了各種異常子類,這部分handleError功能寫起來比Okhttp3方便很多。
缺點:
BasicNetwork 中的statuCode狀態(tài)碼處理有點籠統(tǒng),if (statusCode < 200 || statusCode > 299) 直接throw new IOException(); 另外還有volley 401 錯誤處理
3.2 重試
Okhttp3
通過攔截器RetryAndFollowUpInterceptor來做,因為攔截器是基于OkHttpClient來做的,如果是通用型重試還好,如果Request需要不同個性化重試策略,比如重試次數(shù)有差別,或者是否提供其他重試域名等等。這種情況下RetryAndFollowUpInterceptor就不太好做了,況且他還是final的,默認(rèn)策略不滿足還只能自己重新寫。
Volley
Request.setRetryPolicy來實現(xiàn),這對單一Request個性化定制來說比Okhttp3靈活,它接口為RetryPolicy,有個默認(rèn)實現(xiàn)DefaultRetryPolicy,支持參數(shù)的動態(tài)配置,當(dāng)然也可以自定義。相對比較靈活。
3.3 調(diào)度
Okhttp3
Dispatcher提供了在最大并發(fā)數(shù)64和每個主機最大請求數(shù)5的范圍內(nèi)的一個類似CacheThreadPool的線程池。這種類型線程池比較適用于:高并發(fā)但每個任務(wù)耗時較少的場景。
//準(zhǔn)備隊列
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//執(zhí)行隊列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//最大并發(fā)數(shù)。
private int maxRequests = 64;
//每個主機的最大請求數(shù)。
private int maxRequestsPerHost = 5;
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
那么來看看調(diào)度邏輯:
void enqueue(AsyncCall call) {
synchronized (this) {
//加入雙端準(zhǔn)備隊列
readyAsyncCalls.add(call);
}
promoteAndExecute();
}
將符合條件的calls從readyAsyncCalls移動到runningAsyncCalls里面,在executor service上執(zhí)行它們。
private boolean promoteAndExecute() {
assert (!Thread.holdsLock(this));
List<AsyncCall> executableCalls = new ArrayList<>();
boolean isRunning;
synchronized (this) {
//循環(huán)判斷準(zhǔn)備請求隊列是否還有請求
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall asyncCall = i.next();
//如果執(zhí)行請求的隊列數(shù)量大于等于最大請求數(shù),中止循環(huán)
if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
//小于最大主機請求限制
if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
//從準(zhǔn)備隊列里移除自身。
i.remove();
executableCalls.add(asyncCall);
//添加到執(zhí)行隊列中。
runningAsyncCalls.add(asyncCall);
}
isRunning = runningCallsCount() > 0;
}
//對新添加的任務(wù)嘗試進(jìn)行異步調(diào)用。
for (int i = 0, size = executableCalls.size(); i < size; i++) {
AsyncCall asyncCall = executableCalls.get(i);
asyncCall.executeOn(executorService());
}
return isRunning;
}
對應(yīng)的AsyncCall 就是一個Runnable。
這里可以在源碼的基礎(chǔ)上做兩個層面的自定義調(diào)度優(yōu)先級:
1)線程層面的
可以通過自定義Request傳入priority,在RealCall中獲取對應(yīng)Request的優(yōu)先級并設(shè)置給AsyncCall
final class AsyncCall extends NamedRunnable {
...
@Override
public void run() {
super.run();
//originalRequest 就是傳進(jìn)來的Request
Thread.currentThread().setPriority(originalRequest.priority());
}
2)線程池Queue層面的
executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
new PriorityBlockingQueue<Runnable>(60,new AsycCallComparator<Runnable>()), Util.threadFactory( "OkHttp Dispatcher", false));
這里使用PriorityBlockingQueue,它是一個具有優(yōu)先級的無界隊列,同時需要實現(xiàn)一個有序的比較器:
public class AsycCallComparator<T> implements Comparator {
@Override
public int compare(Object o1, Object o2) {
if ((o1 instanceof RealCall.AsyncCall) && (o2 instanceof RealCall.AsyncCall)) {
RealCall.AsyncCall task1 = (RealCall.AsyncCall) o1;
RealCall.AsyncCall task2 = (RealCall.AsyncCall) o2;
int result = task2.priority() - task1.priority();
return result;
}
return 0;
}
}
Volley
Volley是1個Cache Thread,4個Network Thread。各自都有一個對應(yīng)的PriorityBlockingQueue維護(hù)請求隊列。
/**
* The cache triage queue.
*/
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<>();
/**
* The queue of requests that are actually going out to the network.
*/
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<>();
/**
* Number of network request dispatcher threads to start.
*/
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// Create the cache dispatcher and start it.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// Create network dispatchers (and corresponding threads) up to the pool size.
//mDispatchers.length 即為DEFAULT_NETWORK_THREAD_POOL_SIZE
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
對于優(yōu)先級調(diào)度來說:
Volley本身mCacheQueue 和 mNetworkQueue 就使用的PriorityBlockingQueue,而線程優(yōu)先級就更方便了,重構(gòu)一個RequestQueue的start方法,直接給NetworkDispatcher設(shè)置優(yōu)先級。
public void start(int threadPriority) {
stop(); // Make sure any currently running dispatchers are stopped.
// Create the cache dispatcher and start it.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// Create network dispatchers (and corresponding threads) up to the pool size.
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
try {
networkDispatcher.setPriority(threadPriority);
} catch (Exception e) {
e.printStackTrace();
}
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
最近完成了公司網(wǎng)絡(luò)框架組件的重構(gòu),新增了Okhttp支持,在保證不動業(yè)務(wù)層調(diào)用的情況下,調(diào)整了網(wǎng)絡(luò)庫整體架構(gòu),支持Okhttp和Volley動態(tài)切換,并且從其他Module的耦合中剝離出來,單獨做maven管理。從兩個框架的使用情況來看,常規(guī)請求都是50-100ms量級的速度,兩者不相上下。兩者對get 、post 、multipart post支持都還不錯。后續(xù)再進(jìn)一步看看表現(xiàn)差異以及存在的問題。
