手撕 Volley (一)
手撕 Volley (二)
節(jié)后工作第一天啦租,不知道大家有沒有能夠很好地融入到工作、學(xué)習(xí)當(dāng)中呢。過幾天 NBA 可就要開始了辖试,還有畢設(shè)、實(shí)習(xí)答辯莺债、說好的 dream offer滤蝠,這個(gè)十月注定是忙碌的,啊·······
不扯了缓苛,繼續(xù) read the fucking source code,本篇內(nèi)容包括:
- 網(wǎng)絡(luò)訪問——HurlStack 實(shí)現(xiàn)
- 結(jié)果分發(fā)——ResponseDelivery 接口
- 請(qǐng)求完成——RequestQueue finish方法
- 請(qǐng)求取消——RequestQueue cancelAll方法
- 總結(jié)
網(wǎng)絡(luò)訪問
上回說到邓深,最終的網(wǎng)絡(luò)執(zhí)行還是在 HttpStack 中未桥,而我們又知道,HttpStack 是一個(gè)接口芥备,在 Volley 中他有兩種實(shí)現(xiàn):
- 基于 HttpClient 的 HttpClientStack
- 基于 HttpURLConnection 的 HurlStack
之所以會(huì)用到兩種實(shí)現(xiàn)是由于 HttpURLConnection 在
Android SDK 9 以前有 bug冬耿, 可以說這是歷史原因吧。而我們看 Volley 的配置文件:
可以看到最低支持的 SDK 為8萌壳,在最新的 Android 6.0 中已經(jīng)取消了對(duì) HttpClient 的依賴亦镶,如果你想用 Volley 的話可以修改 Volley 的源碼,或者手動(dòng)添加依賴到 gradle袱瓮。這里我們只看 HurlStack缤骨。
HurlStack
先看類圖
- 定義了一個(gè)內(nèi)部接口 UrlWriter,作用是實(shí)現(xiàn)對(duì)Url的攔截尺借,對(duì)url進(jìn)行一些處理
- 成員變量 SSlSocketFactory绊起,作用是用 SSL 構(gòu)建安全的 Socket 進(jìn)行 HTTPS 通信。
HurlStack 有三個(gè)構(gòu)造方法:
public HurlStack() {
this(null);
}
/**
* @param urlRewriter Rewriter to use for request URLs
*/
public HurlStack(UrlRewriter urlRewriter) {
this(urlRewriter, null);
}
/**
* @param urlRewriter Rewriter to use for request URLs
* @param sslSocketFactory SSL factory to use for HTTPS connections
*/
public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
mUrlRewriter = urlRewriter;
mSslSocketFactory = sslSocketFactory;
}
還記得我們?cè)?a href="http://www.reibang.com/p/33be82da8f25" target="_blank">手撕 Volley (一)
最開始分析的入口函數(shù) Volley 的 newRequestQueue 方法嗎
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
// 省略
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
//省略
}
可以看到燎斩,這里 Volley 默認(rèn)調(diào)用的是最上面那個(gè)構(gòu)造器虱歪,但其實(shí)最上面的構(gòu)造函數(shù)會(huì)調(diào)用有一個(gè)參數(shù)的也就是第二個(gè)構(gòu)造函數(shù),并將參數(shù)值設(shè)為 null栅表,同樣第二個(gè)會(huì)調(diào)用最后一個(gè)有兩個(gè)參數(shù)的構(gòu)造函數(shù)笋鄙,并將參數(shù)設(shè)為 null。也就是將 urlRewriter 和 sslSocketFactory 都初始化為 null怪瓶。
當(dāng)然我們?nèi)绻袛r截 URL 需求或者安全需求需要用到 HTTPS 的話萧落,可以自己寫 HttpStack 實(shí)現(xiàn)需求,然后傳給 Volley 的 newRequestQueue 方法洗贰。
再次感受到 Volley 基于接口編程的強(qiáng)大找岖,膜拜 ing。
最后再來看核心方法 performRequest哆姻,其余的方法都是為它服務(wù)的宣增。
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
throw new IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
response.setEntity(entityFromConnection(connection));
}
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
}
來看流程圖,順序執(zhí)行比較簡單矛缨,偷個(gè)懶爹脾,判斷的地方一筆帶過帖旨。需要注意的是關(guān)于 SSLServerSocketFactory 的調(diào)用是在 openConnection 方法中處理的。
內(nèi)容分發(fā)
上面說到 HttpStack 返回了一個(gè) response灵妨,那么這個(gè) response 是怎么被傳遞給 user 的呢解阅,我們看一下這個(gè)過程,這個(gè)字體對(duì)中文顯示比較差泌霍,索性全都用我蹩腳的英文來展示了货抄。
HttpStack 返回的是 HttpResponse,NetWork 和 NetWorkDispacher 分別對(duì)其做了封裝朱转,具體代碼我們前面的分析手撕 Volley (二)里面都有蟹地,忘記的朋友可以回去再看看。為什么要這么做呢藤为,我們來看 response 的類圖:
HttpResponse 是一個(gè)接口怪与,這里他的具體實(shí)現(xiàn)類為 BasicHttpResponse,實(shí)現(xiàn)比較簡單缅疟,按照 HTTP 報(bào)文的格式封了裝對(duì)象分别,這里我們可以這樣理解,Response 主要是為了給用戶使用存淫,它封裝了對(duì)網(wǎng)絡(luò)請(qǐng)求失敗和成功的回調(diào)耘斩。NetWorkResponse 算是一個(gè)過度,可能不準(zhǔn)確桅咆。大家有更好的理解可以私我括授。
ok,我們看到到了 Response 到了 NetworkDispacher 以后就是 Delivery 大發(fā)神威了轧邪,又是 pase 又是 deliver刽脖,來看一下它的實(shí)現(xiàn),首先還是類圖
ResponseDelivery 故名思意忌愚,就是傳遞響應(yīng),他在這里的實(shí)現(xiàn)類是 ExecutorDelivery却邓,而 RequstQueue 的構(gòu)造器也就是用的 ExcutorDelivery
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
ExcutorDelivery 對(duì)應(yīng)的構(gòu)造函數(shù)
/**
* Creates a new response delivery interface.
* @param handler {@link Handler} to post responses on
*/
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
傳入了一個(gè)主線程的 Looper 創(chuàng)建的 Handler硕糊,然后有一個(gè) Executor 的成員變量,用來向主線程傳遞 response腊徙。
Interface Executor
Android 異步消息處理機(jī)制 讓你深入理解 Looper简十、Handler、Message三者關(guān)系
ExcutorDelivery 最重要的方法就是 PostResponse 了
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
ResponseDeliveryRunnable 是一個(gè)內(nèi)部類撬腾,實(shí)現(xiàn)了 Runnable 接口螟蝙,postResponse 會(huì)調(diào)用他的 run 方法,如下
@Override
public void run() {
// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
// Deliver a normal response or error, depending.
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
// If this is an intermediate response, add a marker, otherwise we're done
// and the request can be finished.
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
// If we have been provided a post-delivery runnable, run it.
if (mRunnable != null) {
mRunnable.run();
}
}
邏輯比較簡單
那到這里響應(yīng)分發(fā)重?fù)?dān)就傳遞到了 request 的身上
/** Listener interface for errors. */
private Response.ErrorListener mErrorListener;
public void deliverError(VolleyError error) {
if (mErrorListener != null) {
mErrorListener.onErrorResponse(error);
}
}
deliverResponse 是一個(gè)抽象類民傻,StringRequest 里面是這樣實(shí)現(xiàn)的胰默。
import com.android.volley.Response.Listener;
//省略
private Listener<String> mListener;
@Override
protected void deliverResponse(String response) {
if (mListener != null) {
mListener.onResponse(response);
}
}
可以看到都是調(diào)用的接口的方法场斑,那誰來實(shí)現(xiàn)接口的方法呢。當(dāng)然是使用 Volley 的我們牵署。請(qǐng)看
StringRequest stringRequest = new StringRequest("http://www.reibang.com",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("TAG", response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error);
}
});
Listener 是在我們使用的時(shí)候才創(chuàng)建傳入到 request 中的漏隐,接口中的回調(diào)函數(shù)自然也是開發(fā)者來實(shí)現(xiàn)。
也就是說我們添加到 requestqueue 中的請(qǐng)求經(jīng)過一系列的處理得到最終的 response 或者 error奴迅,交給開發(fā)者來處理青责。
到這里結(jié)果分發(fā)就分析結(jié)束了。大家應(yīng)該對(duì)手撕 Volley (一)最開始給出的官方流程圖有更深刻的認(rèn)識(shí)了吧取具。
請(qǐng)求完成
上面的 run 方法中可以看到如果請(qǐng)求取消或者數(shù)據(jù)不需要更新就會(huì)調(diào)用 request 的 finish 方法脖隶,
void finish(final String tag) {
if (mRequestQueue != null) {
mRequestQueue.finish(this);
onFinish();
}
if (MarkerLog.ENABLED) {
final long threadId = Thread.currentThread().getId();
if (Looper.myLooper() != Looper.getMainLooper()) {
// If we finish marking off of the main thread, we need to
// actually do it on the main thread to ensure correct ordering.
Handler mainThread = new Handler(Looper.getMainLooper());
mainThread.post(new Runnable() {
@Override
public void run() {
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
});
return;
}
mEventLog.add(tag, threadId);
mEventLog.finish(this.toString());
}
}
/**
* clear listeners when finished
*/
protected void onFinish() {
mErrorListener = null;
}
- 調(diào)用 ReqestQueue 的 finish
- 調(diào)用自己的 onFinish
- 將調(diào)用 finish 傳入的參數(shù) tag 加入日志,然后 finish 掉日志,可以看到,如果當(dāng)前線程不是主線程的話世澜,會(huì)把處理移交給主線程處理啄清。誰知道為什么。枷遂。
這里說一下 Volley 的日志處理,Wolley 的日志處理由 VolleyLog 幫助完成,上類圖:
三層嵌套的內(nèi)部類怎燥,用來管理日志,需要注意的是 MarkerLog 的 finish 方法
public synchronized void finish(String header) {
mFinished = true;
long duration = getTotalDuration();
if (duration <= MIN_DURATION_FOR_LOGGING_MS) {
return;
}
long prevTime = mMarkers.get(0).time;
d("(%-4d ms) %s", duration, header);
for (Marker marker : mMarkers) {
long thisTime = marker.time;
d("(+%-4d) [%2d] %s", (thisTime - prevTime), marker.thread, marker.name);
prevTime = thisTime;
}
}
這段代碼的意思是蜜暑,如果結(jié)束時(shí) request 存在的時(shí)間不在一個(gè)常量值的范圍之內(nèi)铐姚,就將日志取出逐行計(jì)算時(shí)間差調(diào)用 d ( DEBUG ) 輸出日志信息。
接下來繼續(xù)看重頭戲 requestqueue 的 finish 方法
<T> void finish(Request<T> request) {
// Remove from the set of requests currently being processed.
synchronized (mCurrentRequests) {
mCurrentRequests.remove(request);
}
synchronized (mFinishedListeners) {
for (RequestFinishedListener<T> listener : mFinishedListeners) {
listener.onRequestFinished(request);
}
}
if (request.shouldCache()) {
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
if (waitingRequests != null) {
if (VolleyLog.DEBUG) {
VolleyLog.v("Releasing %d waiting requests for cacheKey=%s.",
waitingRequests.size(), cacheKey);
}
// Process all queued up requests. They won't be considered as in flight, but
// that's not a problem as the cache has been primed by 'request'.
mCacheQueue.addAll(waitingRequests);
}
}
}
}
請(qǐng)求取消
最后簡單說一下肛捍,RequestQueue 的 cancelAll 方法
/**
* A simple predicate or filter interface for Requests, for use by
* {@link RequestQueue#cancelAll(RequestFilter)}.
*/
public interface RequestFilter {
public boolean apply(Request<?> request);
}
/**
* Cancels all requests in this queue for which the given filter applies.
* @param filter The filtering function to use
*/
public void cancelAll(RequestFilter filter) {
synchronized (mCurrentRequests) {
for (Request<?> request : mCurrentRequests) {
if (filter.apply(request)) {
request.cancel();
}
}
}
}
/**
* Cancels all requests in this queue with the given tag. Tag must be non-null
* and equality is by identity.
*/
public void cancelAll(final Object tag) {
if (tag == null) {
throw new IllegalArgumentException("Cannot cancelAll with a null tag");
}
cancelAll(new RequestFilter() {
@Override
public boolean apply(Request<?> request) {
return request.getTag() == tag;
}
});
}
自己定義一個(gè) RequestFilter隐绵, 所有符合這個(gè) fiflter 的都將被標(biāo)志為 mCanceled = true;
自己給 request 設(shè)置一個(gè) tag 所有 request.getTag() == tag 的 Request 都會(huì)被標(biāo)志為 mCanceled = true;
到這里 Volley 的主要流程就走了一遍了,感謝閱讀拙毫。
總結(jié)
三遍文章算是把大概的流程捋了一遍依许,通過捋代碼,我發(fā)現(xiàn) Volley 基于接口編程拓展性強(qiáng)缀蹄,邏輯清晰峭跳,層次分明。要學(xué)到大牛們?cè)O(shè)計(jì)開源項(xiàng)目的精髓還需要更加勤奮啊缺前。蛀醉。。衅码。廢話連篇拯刁。。大家一起加油逝段。
手撕 Volley (一)
手撕 Volley (二)