一、Volley框架圖
Volley :Volley 對外暴露的 API,類中只有兩個函數
通過 newRequestQueue(…) 函數新建並啓動一個請求隊列RequestQueue
。
Request<T> :表示一個請求的抽象類。
StringRequest
、JsonRequest
、ImageRequest
都是它的子類,表示某種類型的請求。
也可自定義自己的Request
RequestQueue :表示請求隊列,一個RequestQueue對象包含:
一個CacheDispatcher
(用於處理走緩存請求的調度線程)、
一個
NetworkDispatcher
數組(默認數組大小爲4,用於處理走網絡請求的調度線程),
一個ResponseDelivery
(返回結果分發接口),
在start() 函數啓動時會創建啓動CacheDispatcher
和NetworkDispatchers
。
CacheDispatcher :Cache層中的一個線程,用於調度處理緩存的請求。
啓動後會不斷從緩存請求隊列中取請求處理,隊列爲空則等待,請求處理結束則將結果傳遞給ResponseDelivery
去執行後續處理。
當結果未緩存過、緩存失效或緩存需要刷新的情況下,該請求都需要重新進入NetworkDispatcher
去調度處理。
NetworkDispatcher:NetWork層中的一個線程,用於調度處理走網絡的請求。
啓動後會不斷從網絡請求隊列中取請求處理,隊列爲空則等待,
請求處理結束則將結果傳遞給ResponseDelivery
去執行後續處理,並判斷結果是否要進行緩存。
ResponseDelivery :返回結果分發接口,在創建RequestQueue對象時進行了初始化
在目前只有基於ExecutorDelivery
的在入參
handler 對應線程內進行分發。
HttpStack :處理 Http 請求,返回請求結果。在newRequestQueue中被初始化。
目前 Volley 中有基於 HttpURLConnection 的HurlStack
和
基於 Apache HttpClient 的HttpClientStack
。
上一篇中已經對其如何根據Android版本進行選擇做了解析。
Network :調用HttpStack
處理請求,並將結果轉換爲可被ResponseDelivery
處理的NetworkResponse
。
在newRequestQueue中被初始化
Cache :緩存請求結果,Volley 默認使用的是基於 sdcard 的DiskBasedCache
。
NetworkDispatcher
得到請求結果後判斷是否需要存儲在 Cache,CacheDispatcher
會從
Cache 中取緩存結果。
下面附上每個類之間的關係圖:
RequestQueue mRequestQueue = Volley.newRequestQueue(this);
看一下Volley.newRequestQueue的事務邏輯,Volley類中總共就兩個方法: /**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
*/
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
代碼的事務主體在這裏: /** Default on-disk cache directory. */
private static final String DEFAULT_CACHE_DIR = "volley";
/**
* Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
*
* @param context A {@link Context} to use for creating the cache dir.
* @param stack An {@link HttpStack} to use for the network, or null for default.
* @return A started {@link RequestQueue} instance.
*/
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
//創建cache
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
/** 根據博文http://blog.csdn.net/guolin_blog/article/details/12452307,HurlStack是用HttpURLConnection實現的;
HttpClintStack是由HttpClient實現的;由Android2.3之前的版本宜使用HttpClient,因爲其Bug較少;
Android2.3之後版本宜使用HttpURLConnection,因其較輕量級且API簡單;
故會有此HurlStack和HttpURLConnection的使用分類 */
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
//創建以stack爲參數的Network對象
Network network = new BasicNetwork(stack);
//創建RequestQueue對象
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();//繼續向下分析的入口
return queue;
}
附I) 、HurlStack中的部分代碼,可以看出其是基於HttpURLClient實現的: private static HttpEntity entityFromConnection(HttpURLConnection connection)
對應的HttpClientStack的構造函數可以看出其實基於HttpClient實現的: public HttpClientStack(HttpClient client) {
mClient = client;
}
而兩者都是基於HttpStack接口的: /** An HTTP stack abstraction.*/
public interface HttpStack {
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}
由於Android 2.3版本之前,因爲HttpURLConnection的BUG較多,HttpClient的API已經較完備,故宜使用HttpClient,故這裏版本9之前,選擇使用HttpClientStack;
Android2.3之後版本,HttpURLConnection不斷髮展,因其較爲輕量級,且API使用較爲簡單,其也在不斷優化性能等,故這裏使用基於其的HurlStack;
/**
* A network performing Volley requests over an {@link HttpStack}.
*/
public class BasicNetwork implements Network {
...
private static int DEFAULT_POOL_SIZE = 4096;
protected final HttpStack mHttpStack;
protected final ByteArrayPool mPool;
public BasicNetwork(HttpStack httpStack) {
// If a pool isn't passed in, then build a small default pool that will give us a lot of
// benefit and not use too much memory.
this(httpStack, new ByteArrayPool(DEFAULT_POOL_SIZE));
}
/**
* @param httpStack HTTP stack to be used
* @param pool a buffer pool that improves GC performance in copy operations
*/
public BasicNetwork(HttpStack httpStack, ByteArrayPool pool) {
mHttpStack = httpStack;
mPool = pool;
}
...
}
/** Number of network request dispatcher threads to start. */
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
public RequestQueue(Cache cache, Network network) {
this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE);
}
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
* @param threadPoolSize Number of network dispatcher threads to create
* @param delivery A ResponseDelivery interface for posting responses and errors
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
在這裏創建了之前分析中一個重要的對象:NetworkDispatcher;並且可以看到其類似線程池似的,創建了大小爲threadPoolSize的NetworkDispatcher數組;其中的處理邏輯暫且不看,首先可以知道其是一個線程:<span style="font-size:14px;"> public class NetworkDispatcher extends Thread</span>
/**
* Starts the dispatchers in this queue.
*/
publicvoid 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.
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
/** Stops the cache and network dispatchers.*/
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
}
/**
* The set of all requests currently being processed by this RequestQueue. A Request
* will be in this set if it is waiting in any queue or currently being processed by any dispatcher.
*/
private final Set<Request> mCurrentRequests = new HashSet<Request>();
/**
* Adds a Request to the dispatch queue.
* @param request The request to service
* @return The passed-in request
*/
public Request add(Request request) {
// Tag the request as belonging to this queue and add it to the set of current requests.
request.setRequestQueue(this); //見附I Request設置其對應的RequestQueue
synchronized (mCurrentRequests) { //mCurrentRequests表示當前該RequestQueue持有的requests,由HashSet來保存
mCurrentRequests.add(request);
}
// 爲新添加的request進行一系列的初始化設置
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// 見附II 判斷request是否允許緩存
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
//request如果允許緩存
//Insert request into stage if there's already a request with the same cache key in flight.
synchronized (mWaitingRequests) { // 見附III
String cacheKey = request.getCacheKey();
if (mWaitingRequests.containsKey(cacheKey)) {
// There is already a request in flight. Queue up.
Queue<Request> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} else {
// Insert 'null' queue for this cacheKey, indicating there is now a request in
// flight.
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
附:mCurrentRequests維護了一個正在進行中,尚未完成的請求集合。private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
附I)、Request.setRequestQueue() 字面上可以看出是Request設置其對應的RequestQueue,簡單的setter函數: /** The request queue this request is associated with. */
private RequestQueue mRequestQueue;
/**
* Associates this request with the given queue. The request queue will be notified when this
* request has finished.
*/
public void setRequestQueue(RequestQueue requestQueue) {
mRequestQueue = requestQueue;
}
附II)、request.shouldCache()用以判斷該request是否允許緩存(默認允許,可使用setShouldCache(false)來禁止緩存);如果不允許緩存,則直接將其添加到mNetworkQueue中返回。
/** The queue of requests that are actually going out to the network. */
private final PriorityBlockingQueue<Request> mNetworkQueue = new PriorityBlockingQueue<Request>();
/**
* Staging area for requests that already have a duplicate request in flight.
* <ul>
* <li>containsKey(cacheKey) indicates that there is a request in flight for the given cache
* key.</li>
* <li>get(cacheKey) returns waiting requests for the given cache key. The in flight request
* is <em>not</em> contained in that list. Is null if no requests are staged.</li>
* </ul>
*/
private final Map<String, Queue<Request>> mWaitingRequests = new HashMap<String, Queue<Request>>();
1)對於每個新add的request,先獲取它的CacheKey;
2)如果當前mWaitingRequests不存在當前cachekey,則會put(cacheKey, null);null表示當前Map中已經存在了一個對應cacheKey的請求;
3)如果mWaitingRequests已經存在了對應的cacheKey,通過get(Key)獲取cacheKey對應的Queue;如果Queue爲null,由第二步知,當前cacheKey僅僅對應一個request,則新建對應的Map Value值——Queue<Request>(這裏由LinkedList來實現),然後添加進去即可;
mCacheQueue和
mNetworkQueue是想對應存在的:
mCacheQueue
放在緩存請求隊列中的 Request,將通過緩存獲取數據;
mNetworkQueue
放在網絡請求隊列中的 Request,將通過網絡獲取數據。
private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<?>>();
private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>();
****************************************************************** 下面是彙總,具體參看剩下兩篇 **********************************************************
一、Volley工作流程圖:
繼續從CacheDispatcher和NetworkDispatcher開始看起。
二、CacheDispatcher:
一個線程,用於調度處理走緩存的請求。啓動後會不斷從緩存請求隊列中取請求處理,隊列爲空則等待,請求處理結束則將結果傳遞給ResponseDelivery
去執行後續處理。當結果未緩存過、緩存失效或緩存需要刷新的情況下,該請求都需要重新進入NetworkDispatcher
去調度處理。
(一)看源碼前,先看一下從其成員變量與處理流程:
(1). 成員變量
BlockingQueue<Request<?>>
mCacheQueue
緩存請求隊列BlockingQueue<Request<?>> mNetworkQueue
網絡請求隊列Cache mCache
緩存類,代表了一個可以獲取請求結果,存儲請求結果的緩存ResponseDelivery mDelivery
請求結果傳遞類
(2). 處理流程圖
(3)源碼:
1、構造函數:一系列賦值初始化操作
/**
* Creates a new cache triage dispatcher thread. You must call {@link #start()}
* in order to begin processing.
*/
public CacheDispatcher(BlockingQueue<Request> cacheQueue, BlockingQueue<Request> networkQueue,
Cache cache, ResponseDelivery delivery) {
mCacheQueue = cacheQueue;
mNetworkQueue = networkQueue;
mCache = cache;
mDelivery = delivery;
}
提到使用CacheDispatcher時一定要調用start()方法;而CacheDispatcher的創建與線程start都是在RequestQueue中的add()函數中實現的:
// Create the cache dispatcher and start it.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
2、既然是線程,重點看其run()函數:
@Override
public void run() {
//設置優先級
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Make a blocking call to initialize the cache.
//這裏的Cache其實是DiskBasedCache,見附I
mCache.initialize();
/*****循環處理邏輯******/
while (true) {
try {
// 從緩存隊列 mCacheQueue中取出一個Request;如果mCacheQueue爲空,則阻塞進行忙等待
final Request request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// 如果取出的Request請求已經被取消,則直接finish,處理下一個request
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// 嘗試從緩存中獲取request對應的結果
Cache.Entry entry = mCache.get(request.getCacheKey());
//爲null,表示該cacheKey對應緩存結果不存在,則直接將request添加到mNetworkQueue中
if (entry == null) {
request.addMarker("cache-miss");
mNetworkQueue.put(request);
continue;
}
//如果緩存結果存在,但是已過期,同樣也是將request添加到mNetworkQueue中
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
// 如果命中(hit)找到了對應的緩存結果,則解析其數據爲Response並返回給該request
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
// 還需判斷緩存結果是否時間過久已經不新鮮,是否需要refresh
if (!entry.refreshNeeded()) {
// 不需要Refresh,則直接由mDelivery提交給相應的request
mDelivery.postResponse(request, response);
} else {
// 如果已經不新鮮,mDelivery依舊提交結果給request,
// 但同時要將Request傳遞給mNetworkQueue進行新鮮度驗證
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
// Mark the response as intermediate.
response.intermediate = true;
// Post the intermediate response back to the user and have
// the delivery then forward the request along to the network.
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
}
}
});
}
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
}
}
附I)這裏的Cache實際是DiskBasedCache,mCache.initialize()實際上調用的是DiskBasedCache.initialize():
/**
* Initializes the DiskBasedCache by scanning for all files currently in the
* specified root directory. Creates the root directory if necessary.
*/
public synchronized void initialize()
三、NetworkDispatcher
一個線程,用於調度處理網絡的請求。啓動後會不斷從網絡請求隊列中取請求處理,隊列爲空則等待,請求處理結束則將結果傳遞給 ResponseDelivery 去執行後續處理,並判斷結果是否要進行緩存。
(1). 成員變量
BlockingQueue<Request<?>>
mQueue
網絡請求隊列Network mNetwork
網絡類,代表了一個可以執行請求的網絡Cache mCache
緩存類,代表了一個可以獲取請求結果,存儲請求結果的緩存ResponseDelivery mDelivery
請求結果傳遞類,可以傳遞請求的結果或者錯誤到調用者
(2). 處理流程圖
(3).先看其構造函數,與CacheDispatcher的邏輯處理大致相同:
/**
* Creates a new network dispatcher thread. You must call {@link #start()}
* in order to begin processing.
*/
public NetworkDispatcher(BlockingQueue<Request> queue,
Network network, Cache cache,
ResponseDelivery delivery) {
mQueue = queue;
mNetwork = network;
mCache = cache;
mDelivery = delivery;
}
(4).其run()函數:
@Override
public void run() {
//設爲後臺進程
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request request;
/*********循環處理邏輯********/
while (true) {
try {
// 從網絡請求隊列中取出request,同理mQueue爲空時,也是忙等待
request = mQueue.take();
} catch (InterruptedException e) {
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
// 如果取出的Request請求已經被取消,則直接finish,處理下一個request
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
// Tag the request (if API >= 14)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
// 通過Network執行Request,獲得NetworkResponse,故網絡請求的處理邏輯應該都封裝在了Network中
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// 用來驗證新鮮度,(notModified=304)響應爲304且請求已經有了Response傳輸情況
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// 將NetworkResponse解析爲Response
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// 如果request可以被緩存,並且其請求實體補位空,則添加到mCache中
// TODO: Only update cache metadata instead of entire record for 304s.
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
// 傳輸Response
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
mDelivery.postError(request, new VolleyError(e));
}
}
}
一、Volley工作流程圖:
NetworkResponse networkResponse = mNetwork.performRequest(request);
看一下mNetwork的定義:(定義在NetworkDispatcher中)
/** The network interface for processing requests. */
private final Network mNetwork;
NetworkDispatcher.mNetwork初始化發生在RequestQueue.start()中:
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
而RequestQueue.mNetwork是在其構造函數中傳入的:
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
由前面分析知RequestQueue的構建是在Volley.newRequestQueue中實現的:
//創建以stack爲參數的Network對象
Network network = new BasicNetwork(stack);
//創建RequestQueue對象
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();//繼續向下分析的入口
HttpStack
處理請求,並將結果轉換爲可被ResponseDelivery
處理的NetworkResponse
。 @Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = new HashMap<String, String>();
try {
/** 忽略網絡處理的細節*/
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
/**執行網絡請求
* 這裏調用了HttpStack.performRequest,並得到一個HttpResponse返回結果*/
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
/**新鮮度驗證:
* 304 Not Modified:客戶端有緩衝的文件併發出了一個條件性的請求
* (一般是提供If-Modified-Since頭表示客戶只想比指定日期更新的文檔)。
* 服務器告訴客戶,原來緩衝的文檔還可以繼續使用。*/
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
/** 解析成NetworkResponse,返回*/
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
request.getCacheEntry().data, responseHeaders, true);
}
// 判斷responses是否有實體信息,一些響應如204,並不包含content,所以需要驗證
if (httpResponse.getEntity() != null) {
//實體信息轉化成byte[]
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// 無實體信息情況
responseContents = new byte[0];
}
// 超時情況處理.
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine);
if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
} catch (SocketTimeoutException e) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException e) {
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
...
}
}
}
總結一下Network.performRequest所做的工作:
1、由傳入的HttpStack對象執行網絡請求:mHttpStack.performRequest()
2、解析響應結果,將HttpResponse解析成NetworkResponse;
3、對返回結果進行新鮮度驗證(304)
4、將response的實體信息轉化爲byte數組
5、超時情況處理,如果發生超時,認證失敗等錯誤,進行重試操作(attemptRetryOnException),直到成功、拋出異常(不滿足重試策略等)結束。
attemptRetryOnException()是根據重試策略進行請求重試操作:
/**
* Attempts to prepare the request for a retry. If there are no more attempts remaining in the
* request's retry policy, a timeout exception is thrown.
*/
private static void attemptRetryOnException(String logPrefix, Request<?> request,
VolleyError exception) throws VolleyError {
RetryPolicy retryPolicy = request.getRetryPolicy();
int oldTimeout = request.getTimeoutMs();
try {
retryPolicy.retry(exception);
} catch (VolleyError e) {
request.addMarker(
String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
throw e;
}
request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
}
public interface HttpStack {
/**
* Performs an HTTP request with the given parameters.
* <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,
* and the Content-Type header is set to request.getPostBodyContentType().</p>
* @param request the request to perform
* @param 發起請求之前,添加額外的請求 Headers {@link Request#getHeaders()}
*/
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}
2、HttpClientStack(使用HttpClient來實現)
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);//見附一
addHeaders(httpRequest, additionalHeaders);
addHeaders(httpRequest, request.getHeaders());
onPrepareRequest(httpRequest);// Nothing.空函數,用於重寫;該函數在request被excute之前被調用
//一些網絡設置
HttpParams httpParams = httpRequest.getParams();
int timeoutMs = request.getTimeoutMs();
// TODO: Reevaluate this connection timeout based on more wide-scale
// data collection and possibly different for wifi vs. 3G.
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
return mClient.execute(httpRequest);
}
附一:createHttpRequest函數:
/**
* 根據傳進來的request來構造合適的HttpUriRequest
*/
static HttpUriRequest createHttpRequest(Request<?> request,
Map<String, String> additionalHeaders) throws AuthFailureError {
switch (request.getMethod()) {
case Method.DEPRECATED_GET_OR_POST: {
// This is the deprecated way that needs to be handled for backwards compatibility.
// If the request's post body is null, then the assumption is that the request is
// GET. Otherwise, it is assumed that the request is a POST.
byte[] postBody = request.getPostBody();
if (postBody != null) {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());
HttpEntity entity;
entity = new ByteArrayEntity(postBody);
postRequest.setEntity(entity);
return postRequest;
} else {
return new HttpGet(request.getUrl());
}
}
/***********一般較多使用的是POST與GET,其等同於HttpClient的一般使用流程***************/
case Method.GET:
return new HttpGet(request.getUrl());
case Method.DELETE:
return new HttpDelete(request.getUrl());
case Method.POST: {
HttpPost postRequest = new HttpPost(request.getUrl());
//這裏就看到了前面實現Request時,重寫getBodyContentType()函數的意義
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(postRequest, request);
return postRequest;
}
case Method.PUT: {
HttpPut putRequest = new HttpPut(request.getUrl());
putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(putRequest, request);
return putRequest;
}
default:
throw new IllegalStateException("Unknown request method.");
}
}
3、HurlStack(由HttpURLConnection來實現) @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);
//UrlRewriter見附一
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
thrownew IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
/**************HttpURLConnection的一般使用流程*******************/
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.
thrownew IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
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;
}
附一:UrlRewriter
/** 對URLs在使用前進行重寫轉換*/
public interface UrlRewriter {
/**
* Returns a URL to use instead of the provided one, or null to indicate
* this URL should not be used at all.
*/
public String rewriteUrl(String originalUrl);
}
參數mUrlRewriter通過HttpStack的構造函數傳入進來,故可以自行進行定義: public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
mUrlRewriter = urlRewriter;
mSslSocketFactory = sslSocketFactory;
}
NetworkResponse networkResponse = mNetwork.performRequest(request);
int statusCode
Http
響應狀態碼byte[]
data
Body 數據Map<String, String> headers
響應 Headersboolean notModified
表示是否爲 304 響應long networkTimeMs
請求耗時
public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
boolean notModified) {
this.statusCode = statusCode;
this.data = data;
this.headers = headers;
this.notModified = notModified;
}
public NetworkResponse(byte[] data) {
this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false);
}
public NetworkResponse(byte[] data, Map<String, String> headers) {
this(HttpStatus.SC_OK, data, headers, false);
}
3)回顧一下前面分析的設計NetworkResponse的類之間數據的傳遞關係:
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
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);
}
};
}
可以看到很簡單,就是使用主線程的Looper構建一個Handler,下面所有的post操作都是調用這個Handler來執行Runnable;比如:
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
將傳遞來的Response轉化爲ResponseDeliveryRunnable ,顯然這是一個Runnable;
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
}
@SuppressWarnings("unchecked")
@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();
}
}
}
在這個子線程中,轉而調用 Request來deliverResponse:
以StringRequest爲例,來看這個函數:
@Override
protected void deliverResponse(String response) {
if (mListener != null) {
mListener.onResponse(response);
}
}
這個Listener就是自己在定義Request的時候聲明的ResponseListener,可以看到這個Listener工作在子線程中,所以如果要更新界面,注意使用Handler把消息傳遞主線程進行處理。//創建ImageLoader
imageLoader = new ImageLoader(httpUtils.getRequestQueue(), imageCache);
public ImageLoader(RequestQueue queue, ImageCache imageCache) {
mRequestQueue = queue;
mCache = imageCache;
}
// 獲取最大內存緩存大小
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
MAX_CACHE_SIZE = maxMemory / 8; // 定義爲應用最大緩存的1/8
mImageLruCache = new LruCache<String, Bitmap>(MAX_CACHE_SIZE){
@Override
protected int sizeOf(String url, Bitmap bitmap){
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
};
// 創建ImageCache
imageCache = new ImageLoader.ImageCache() {
@Override
public void putBitmap(String url, Bitmap bitmap) {
mImageLruCache.put(url, bitmap);
}
@Override
public Bitmap getBitmap(String url) {
return mImageLruCache.get(url);
}
};
使用LruCache來實現ImageCache接口,實現圖片的內存緩存:public interface ImageCache {
public Bitmap getBitmap(String url);
public void putBitmap(String url, Bitmap bitmap);
}
加載圖片時的用法:imageListener = ImageLoader.getImageListener(myImageView, default_pg, failed_pg);
imageLoader.get(imageUrl, imageListener);
來到ImageLoader#get:
public ImageContainer get(String requestUrl, final ImageListener listener) {
return get(requestUrl, listener, 0, 0);
}
public ImageContainer get(String requestUrl, ImageListener imageListener,
int maxWidth, int maxHeight) {
return get(requestUrl, imageListener, maxWidth, maxHeight, ImageView.ScaleType.CENTER_INSIDE);
}
public ImageContainer get(String requestUrl, ImageListener imageListener,
int maxWidth, int maxHeight, ImageView.ScaleType scaleType) {
// 如果操作不是在主線程,則直接拋出異常
throwIfNotOnMainThread();
// 爲圖片的URL創建一個特定的cacheKey,注意這個cache還和圖片的大小及scaleType相關
final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType);
// 這裏會使用自定義的LruCache去獲取一個Bitmap實例
Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
// 如果緩存中已經存在,則直接返回
if (cachedBitmap != null) {
// Return the cached bitmap.
ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);
imageListener.onResponse(container, true);
return container;
}
// 如果緩存中不存在,則進行獲取
ImageContainer imageContainer =
new ImageContainer(null, requestUrl, cacheKey, imageListener);
// 通知Observer這時可以使用默認的圖片
imageListener.onResponse(imageContainer, true);
// 判斷是否已經有了一個相同的請求在等待
BatchedImageRequest request = mInFlightRequests.get(cacheKey);
if (request != null) {
// If it is, add this request to the list of listeners.
request.addContainer(imageContainer);
return imageContainer;
}
// 創建一個Request,重複之前的流程
Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType,
cacheKey);
mRequestQueue.add(newRequest);
mInFlightRequests.put(cacheKey,
new BatchedImageRequest(newRequest, imageContainer));
return imageContainer;
}
處理邏輯大致和前面的addRequest相同,首先判斷緩存中是否已經存在該url對應的bitmap,如果存在直接返回;如果不存在,先判斷是否已經有了一個相同的請求在等待,如果是,把這個請求添加到監聽者鏈表中;如果不存在,則創建一個Request<Bitmap>,添加到RequestQueue中,從網絡中去獲取;從網絡中獲取的流程和前面分析的相同。
先來看Request<Bitmap>:
protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight,
ScaleType scaleType, final String cacheKey) {
return new ImageRequest(requestUrl, new Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
onGetImageSuccess(cacheKey, response);
}
}, maxWidth, maxHeight, scaleType, Config.RGB_565, new ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
onGetImageError(cacheKey, error);
}
});
}
實際上返回一個ImageRequest類型,來看其請求成功的響應:即把獲得的圖片存儲到緩存中;
protected void onGetImageSuccess(String cacheKey, Bitmap response) {
// 把獲取到的圖片存儲到緩存中
mCache.putBitmap(cacheKey, response);
// 可以看到如果是多個相同請求在等待,則可以同時進行更新處理
BatchedImageRequest request = mInFlightRequests.remove(cacheKey);
if (request != null) {
// Update the response bitmap.
request.mResponseBitmap = response;
// Send the batched response
batchResponse(cacheKey, request);
}
}
最後NetWork執行的結果會封裝成NetWorkResponse,通過ResponseDelivery進行轉發,這個類最後會調用Request中deliverResponse方法:
@Override
protected void deliverResponse(Bitmap response) {
mListener.onResponse(response);
}
這個Listener就是最初定義的ImageListener:
public static ImageListener getImageListener(final ImageView view,
final int defaultImageResId, final int errorImageResId) {
return new ImageListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (errorImageResId != 0) {
view.setImageResource(errorImageResId);
}
}
@Override
public void onResponse(ImageContainer response, boolean isImmediate) {
if (response.getBitmap() != null) {
view.setImageBitmap(response.getBitmap());
} else if (defaultImageResId != 0) {
view.setImageResource(defaultImageResId);
}
}
};
}
可以看到這裏最終給View空間設置了圖片,以上就是Volley實現圖片加載的流程。