volley
是一個非常流行的Android
開源框架伸眶,自己平時也經(jīng)常使用它,但自己對于它的內(nèi)部的實現(xiàn)過程并沒有進行太多的深究灸撰。所以為了以后能更通透的使用它伞插,了解它的實現(xiàn)是一個非常重要的過程罚缕。自己有了一點研究艇纺,做個筆記同時與大家一起分享。期間自己也畫了一張圖怕磨,希望能更好的幫助我們理解其中的步驟與原理喂饥。如下:
開始看可能會一臉懵逼,我們先結(jié)合源碼一步一步來肠鲫,現(xiàn)在讓我們一起進入Volley
的源碼世界员帮,來解讀大神們的編程思想。
newRequestQueue
如果使用過Volley
的都知道导饲,不管是進行網(wǎng)絡(luò)請求還是什么別的圖片加載捞高,首先都要創(chuàng)建好一個RequestQueue
public static final RequestQueue volleyQueue = Volley.newRequestQueue(App.mContext);
而RequestQueue
的創(chuàng)建自然離不開Volley
中的靜態(tài)方法newRequestQueue
,從上面的圖片也能知道首先進入點是newRequestQueue
,好了現(xiàn)在我們來看下該方法中到底做了什么:
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
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) {
}
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));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
從上面的源碼中我們能發(fā)現(xiàn)有一個stack
渣锦,它代表著網(wǎng)絡(luò)請求通信HttpClient
與HttpUrlConnection
硝岗,但我們一般都是默認設(shè)置為null
。因為它會默認幫我們進判斷選擇更適合的袋毙。當手機版本大于等于9
時會使用HurlStack
它里面使用HttpUrlConnection
進行實現(xiàn)通信的型檀,而小于9
時則創(chuàng)建HttpClientStack
,它里面自然是HttpClient
的實現(xiàn)听盖。通過BasicNetwork
構(gòu)造成Network
來進行傳遞使用胀溺;在這之后構(gòu)建了RequestQueue
裂七,其中幫我們構(gòu)造了一個緩存new DiskBasedCache(cacheDir)
,默認為5MB
,緩存目錄為volley
仓坞。所以我們能在data/data/應(yīng)用包名/volley
下找到緩存背零。最后調(diào)用其start
方法,并返回RequestQueue
无埃。這就是我們前面第一段代碼的內(nèi)部實現(xiàn)徙瓶。下面我們進入RequestQueue
中的start
方法看下它到底做了什么呢?
RequestQueue
在RequestQueue
中通過this
調(diào)用自身來默認幫我們調(diào)用了下面的構(gòu)造函數(shù)
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
cache
后續(xù)在NetworkDispatcher
中會幫我們進行response.cacheEntry
的緩存嫉称,netWork
是前面的根據(jù)版本所封裝的通信侦镇,threadPoolSize
線程池大小默認為4
,delivery
,是ExecutorDelivery
作用在主線程,在最后對請求響應(yīng)的分發(fā)澎埠。
start
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.
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
在start
方法中我們發(fā)現(xiàn)它實現(xiàn)了兩個dispatch
,一個是CacheDispatcher
緩存派遣,另一個是networkDispatcher
進行網(wǎng)絡(luò)派遣虽缕,其實他們都是Thread
,所以都調(diào)用了他們的start
方法蒲稳。其中networkDispatcher
默認構(gòu)建了4
個,相當于包含4
個線程的線程池∥榕桑現(xiàn)在我們先不去看他們內(nèi)部的run
方法到底實現(xiàn)了什么江耀,我們還是接著看RequestQueue
中我們頻繁使用的add
方法。
add
public <T> Request<T> add(Request<T> request) {
// Tag the request as belonging to this queue and add it to the set of current requests.
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// Process requests in the order they are added.
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// If the request is uncacheable, skip the cache queue and go straight to the network.
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// Insert request into stage if there's already a request with the same cache key in flight.
synchronized (mWaitingRequests) {
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;
}
}
我們結(jié)合代碼與前面的圖诉植,首先會為當前的request
設(shè)置RequestQueue
,并且根據(jù)情況同步是否是當前正在進行的請求祥国,加入到mCurrentRequests
中,看11
行代碼晾腔,之后對當前request
進行判斷是否需要緩存(默認實現(xiàn)是true
舌稀,如果不需要可調(diào)用request.setShouldCache()
進行設(shè)置),如果不需要則直接加入到前面的mNetworkQueue
中,它會在CacheDispatcher
與NetworkDispatcher
中做相應(yīng)的處理灼擂,然后返回request
壁查。如果需要緩存,看16行代碼剔应,則對mWaitingRequests
中是否包含cacheKey
進行相應(yīng)的處理睡腿。其中cacheKey
為請求的url
。最后再加入到緩存隊列mCacheQueue
中峻贮。
finish
細心的人會發(fā)現(xiàn)當對應(yīng)cacheKey
的value
不為空時席怪,創(chuàng)建了LinkedList
即Queue
,只是將request
加入到了Queue
中,只是更新了mWaitingRequests
中相應(yīng)的value
但并沒有加入到mCacheQueue
中纤控。其實不然挂捻,因為后續(xù)會調(diào)用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);
}
}
}
}
看14
、22
行代碼船万,正如上面我所說刻撒,會將Queue
中的request
全部加入到mCacheQueue
中惜辑。
好了RequestQueue
的主要源碼差不多就這些,下面我們進入CacheDispatcher
的源碼分析,看它究竟如何工作的呢疫赎?
CacheDispatcher
前面提到了它與NetworkDispatcher
本質(zhì)都是Thread
盛撑,那么我們自然是看run
方法
run
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Make a blocking call to initialize the cache.
mCache.initialize();
while (true) {
try {
// Get a request from the cache triage queue, blocking until
// at least one is available.
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// If the request has been canceled, don't bother dispatching it.
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// Attempt to retrieve this item from cache.
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
mNetworkQueue.put(request);
continue;
}
// If it is completely expired, just send it to the network.
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
// Completely unexpired cache hit. Just deliver the response.
mDelivery.postResponse(request, response);
} else {
// Soft-expired cache hit. We can deliver the cached response,
// but we need to also send the request to the network for
// refreshing.
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) {
// Not much we can do about this.
}
}
});
}
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
}
}
看起來很多,我們結(jié)合圖來挑主要的看捧搞。首先創(chuàng)建了一個無限循環(huán)一直在監(jiān)視著request
的變化抵卫。從緩存隊列mCacheQueue
中獲取request
,如果該請求是cancle
了,調(diào)用request.finish()
清除相應(yīng)數(shù)據(jù)并進行下一個請求的操作胎撇,否則從前面提到的mCache
中獲取Cache.Entry
介粘。如果不存在或者已經(jīng)過期,將請求加入到網(wǎng)絡(luò)隊列中mNetWorkQueue
晚树,進行后續(xù)的網(wǎng)絡(luò)請求姻采。如果存在(41
行代碼)則進行request.parseNetworkResponse()
解析出response
,不同的request
對應(yīng)不同的解析方法。例如StringRequest
與JsonObjectRequest
有各自的解析實現(xiàn)爵憎。再看45
行慨亲,發(fā)現(xiàn)不管entry
是否需要更新的,都會進一步對response
進行mDelivery.postResponse(request, response)
遞送宝鼓,不同的是需要更新的話重新設(shè)置request
的entry
與加入到mNetworkQueue
中刑棵,也就相當與重新進行網(wǎng)絡(luò)請求一遍。那么再回到遞送的階段愚铡,前面已經(jīng)提到在創(chuàng)建RequestQueue
是實現(xiàn)了ExecutorDelivery
,mDelivery.postResponse
就是其中的方法蛉签。我們來看一下
ExecutorDelivery
在這里創(chuàng)建了一個Executor
,對后面進行遞送,作用在主線程
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);
}
};
}
postResponse
@Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
這個方法就簡單了就是調(diào)用execute
進行執(zhí)行沥寥,在進入ResponseDeliveryRunnable
的run
看它如何執(zhí)行
ResponseDeliveryRunnable
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();
}
}
主要是第9
行代碼碍舍,對于不同的響應(yīng)做不同的遞送,deliverResponse
與deliverError
內(nèi)部分別調(diào)用的就是我們非常熟悉的Listener
中的onResponse
與onErrorResponse
方法邑雅,進而返回到我們對網(wǎng)絡(luò)請求結(jié)果的處理函數(shù)片橡。
這就是整個的緩存派遣,簡而言之蒂阱,存在請求響應(yīng)的緩存數(shù)據(jù)就不進行網(wǎng)絡(luò)請求锻全,直接調(diào)用緩存中的數(shù)據(jù)進行分發(fā)遞送。反之執(zhí)行網(wǎng)絡(luò)請求录煤。
下面我來看下NetworkDispatcher
是如何處理的
NetworkDispatcher
run
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
Request<?> request;
try {
// Take a request from the queue.
request = mQueue.take();
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
try {
request.addMarker("network-queue-take");
// If the request was cancelled already, do not perform the
// network request.
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
addTrafficStatsTag(request);
// Perform the network request.
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
// If the server returned 304 AND we delivered a response already,
// we're done -- don't deliver a second identical response.
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
continue;
}
// Parse the response here on the worker thread.
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
// Write to cache if applicable.
// 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");
}
// Post the response back.
request.markDelivered();
mDelivery.postResponse(request, response);
} catch (VolleyError volleyError) {
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
parseAndDeliverNetworkError(request, volleyError);
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
VolleyError volleyError = new VolleyError(e);
volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
mDelivery.postError(request, volleyError);
}
}
}
在這里也創(chuàng)建了一個無限循環(huán)的while
,同樣也是先獲取request
鳄厌,不過是從mQueue
中,即前面多次出現(xiàn)的mNetWorkQueue
,通過看代碼發(fā)現(xiàn)一些實現(xiàn)跟CacheDispatcher
中的類似妈踊。也正如圖片中所展示的一樣了嚎,(23
行)如何請求取消了,直接finish
;否則進行網(wǎng)絡(luò)請求歪泳,調(diào)用(31
行)mNetwork.performRequest(request)
萝勤,這里的mNetWork
即為前面RequestQueue
中對不同版本進行選擇的stack
的封裝,分別調(diào)用HurlStack
與HttpClientStack
各自的performRequest
方法呐伞,該方法中構(gòu)造請求頭與參數(shù)分別使用HttpClient
或者HttpUrlConnection
進行網(wǎng)絡(luò)請求敌卓。我們再來看42
行,是不是很熟悉伶氢,與CacheDispatcher
中的一樣進行response
進行解析趟径,然后如果需要緩存就加入到緩存中,最后(54
行)再調(diào)用mDelivery.postResponse(request, response)
進行遞送癣防。至于后面的剩余的步驟與CacheDispatcher
中的一模一樣蜗巧,這里就不多累贅了。
好了蕾盯,
Volley
的源碼解析先就到這里了幕屹,我們再回過去看那張圖是不是感覺很清晰了呢?
總結(jié)
我們來對使用Volley
網(wǎng)絡(luò)請求做個總結(jié)
- 通過
newRequestQueue
初始化與構(gòu)造RequestQueue
- 調(diào)用
RequestQueue
中的add
方法添加request
到請求隊列中 - 緩存派遣,先進行
CacheDispatcher
,判斷緩存中是否存在级遭,有則解析response
望拖,再直接postResponse
遞送,否則進行后續(xù)的網(wǎng)絡(luò)請求 - 網(wǎng)絡(luò)派遣装畅,
NetworkDispatcher
中進行相應(yīng)的request
請求靠娱,解析response
如設(shè)置了緩存就將結(jié)果保存到cache
中,再進行最后的postResponse
遞送掠兄。
如果有所幫助歡迎關(guān)注我的下一次解析
更多分享:個人博客