Picasso原始碼分析(六):BitmapHunter與請求結果的處理
Picasso原始碼分析(一):單例模式、建造者模式、面向介面程式設計
Picasso原始碼分析(二):預設的下載器、快取、執行緒池和轉換器
Picasso原始碼分析(三):快照功能實現和HandlerThread的使用
Picasso原始碼分析(四):不變模式、建造者模式和Request的預處理
Picasso原始碼分析(五):into方法追本溯源和責任鏈模式建立BitmapHunter
Picasso原始碼分析(六):BitmapHunter與請求結果的處理
into方法流程回顧
into方法首先檢查了是否在主執行緒,控制元件是否為null,使用者有沒有設定uri或者資源id,是否呼叫了fit方法自適應壓縮。接著根據into方法呼叫時間建立Request請求,併為該請求設定快取用的key。接著判斷是否要讀記憶體快取,可以讀記憶體快取並且命中的話就將快取中的圖片顯示在控制元件然後返回,否則開啟網路請求。網路請求的過程中暫時顯示佔點陣圖。
網路請求的過程就是針對此次請求封裝成一個ImageViewAction的請求動作,通過Picasso將此動作遞交給排程器dispatcher,在dispatcher中進行執行緒切換,在工作執行緒中處理該動作。處理的方法就是為這個動作建立一個BitmapHunter(實現了Runnable介面),然後把這個BitmapHunter交給執行緒池去處理。
BitmapHunter分析
BitmapHunter作為一個Runnable,是要交付給執行緒池執行的,所以重點關注實現的run方法。
@Override public void run() {
try {
updateThreadName(data);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_EXECUTING, getLogIdsForHunter(this));
}
result = hunt();
if (result == null) {
dispatcher.dispatchFailed(this);
} else {
dispatcher.dispatchComplete(this);
}
} catch (Downloader.ResponseException e) {
if (!e.localCacheOnly || e.responseCode != 504) {
exception = e;
}
dispatcher.dispatchFailed(this);
} catch (NetworkRequestHandler.ContentLengthException e) {
exception = e;
dispatcher.dispatchRetry(this);
} catch (IOException e) {
exception = e;
dispatcher.dispatchRetry(this);
} catch (OutOfMemoryError e) {
StringWriter writer = new StringWriter();
stats.createSnapshot().dump(new PrintWriter(writer));
exception = new RuntimeException(writer.toString(), e);
dispatcher.dispatchFailed(this);
} catch (Exception e) {
exception = e;
dispatcher.dispatchFailed(this);
} finally {
Thread.currentThread().setName(Utils.THREAD_IDLE_NAME);
}
}
run方法在設定了執行緒名後,直接呼叫hunt方法獲取請求結果。如果hunt方法返回null,說明請求失敗,呼叫排程器dispatchFailed方法,否則說明請求成功,呼叫排程器的dispatchComplete方法。當然在網路請求的過程中會出現各種異常,run方法均一一捕獲並處理。
在finally語句塊中會重置執行緒名,表示該執行緒任務已經完成。
hunt方法分析
具體的網路請求過程是交給了hunt方法處理,該方法返回請求到的Bitmap,網路請求中出現的異常直接丟擲,交給呼叫者也就是run方法處理。
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
if (shouldReadFromMemoryCache(memoryPolicy)) {
bitmap = cache.get(key);
if (bitmap != null) {
stats.dispatchCacheHit();
loadedFrom = MEMORY;
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
}
return bitmap;
}
}
data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
RequestHandler.Result result = requestHandler.load(data, networkPolicy);
if (result != null) {
loadedFrom = result.getLoadedFrom();
exifRotation = result.getExifOrientation();
bitmap = result.getBitmap();
// If there was no Bitmap then we need to decode it from the stream.
if (bitmap == null) {
InputStream is = result.getStream();
try {
bitmap = decodeStream(is, data);
} finally {
Utils.closeQuietly(is);
}
}
}
if (bitmap != null) {
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId());
}
stats.dispatchBitmapDecoded(bitmap);
if (data.needsTransformation() || exifRotation != 0) {
synchronized (DECODE_LOCK) {
if (data.needsMatrixTransform() || exifRotation != 0) {
bitmap = transformResult(data, bitmap, exifRotation);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId());
}
}
if (data.hasCustomTransformations()) {
bitmap = applyCustomTransformations(data.transformations, bitmap);
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_TRANSFORMED, data.logId(), "from custom transformations");
}
}
}
if (bitmap != null) {
stats.dispatchBitmapTransformed(bitmap);
}
}
}
return bitmap;
}
hunt方法首先根據記憶體策略判斷是不是可以讀取快取,可以的話就直接讀快取,快取命中直接返回命中的bitmap,否則需要進行網路請求。
請求前先設定快取策略,retryCount為0的話表示只能讀取快取不能盡情網路請求,否則的話快取策略就用Request的快取策略。
data.networkPolicy = retryCount == 0 ? NetworkPolicy.OFFLINE.index : networkPolicy;
接著就會呼叫RequestHandler實現類的load方法進行具體的請求過程。RequestHandler有多個實現類。
BitmapHunter使用哪一個RequestHandler具體在建立BitmapHunter的時候已經通過責任鏈模式指定
for (int i = 0, count = requestHandlers.size(); i < count; i++) {
RequestHandler requestHandler = requestHandlers.get(i);
if (requestHandler.canHandleRequest(request)) {
return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
}
}
如果是網路請求的話,實際上是交給了NetworkRequestHandler進行網路請求。NetworkRequestHandler的load方法是直接交給了downloader的load方法,downloader的型別是Downloader型別的介面,Downloader介面有兩個實現類,基於okhttp的OkHttpDownloader和基於HttpURLConnection的UrlConnectionDownloader。
@Override
public Result load(Request request, int networkPolicy) throws IOException {
Response response = downloader.load(request.uri, request.networkPolicy);
...
請求成功的話會對請求到的結果進行處理,如果請求的結果中沒有Bitmap物件,那麼就從流中把Bitmap解析出來。
解析到Bitmap後,還要對Bitmap進行各種變換處理,最終將變換處理後的Bitmap返回。
請求成功後的處理
hunt方法上的請求結果會反饋給排程器
result = hunt();
if (result == null) {
dispatcher.dispatchFailed(this);
} else {
dispatcher.dispatchComplete(this);
}
排程器通過handler切換執行緒在工作執行緒對請求結果進行處理
void dispatchComplete(BitmapHunter hunter) {
handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
}
@Override public void handleMessage(final Message msg) {
switch (msg.what) {
...
case HUNTER_COMPLETE: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performComplete(hunter);
break;
}
...
void performComplete(BitmapHunter hunter) {
if (shouldWriteToMemoryCache(hunter.getMemoryPolicy())) {
cache.set(hunter.getKey(), hunter.getResult());
}
hunterMap.remove(hunter.getKey());
batch(hunter);
if (hunter.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_BATCHED, getLogIdsForHunter(hunter), "for completion");
}
}
performComplete方法先根據快取策略進行快取的更新或儲存,接著從map中刪除此請求。接下來有個問題,此時是在工作執行緒進行的處理,如果要將請求結果顯示在控制元件上,需要切換到UI主執行緒。如果通過handler進行執行緒切換,那麼handler應該和被切換到的執行緒的looper繫結在一起,因此需要一個和主執行緒的looper繫結在一起的handler進行執行緒切換,將結果切換到主執行緒。
接下來看batch方法的處理
private void batch(BitmapHunter hunter) {
if (hunter.isCancelled()) {
return;
}
batch.add(hunter);
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
}
}
batch方法給工作執行緒的handler傳送了HUNTER_DELAY_NEXT_BATCH訊息,所以在工作執行緒的handleMessage方法中對此訊息進行處理
case HUNTER_DELAY_NEXT_BATCH: {
dispatcher.performBatchComplete();
break;
}
那麼應該是在performBatchComplete()方法中完成了執行緒的切換
void performBatchComplete() {
List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
batch.clear();
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
logBatch(copy);
}
果不其然,這裡通過mainThreadHandler給主執行緒傳送了HUNTER_BATCH_COMPLETE訊息,那麼在主執行緒的handleMessage方法中會處理此訊息
case HUNTER_BATCH_COMPLETE: {
@SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, n = batch.size(); i < n; i++) {
BitmapHunter hunter = batch.get(i);
hunter.picasso.complete(hunter);
}
break;
}
而在主執行緒中呼叫的complete方法中,會呼叫deliverAction(result, from, join)方法,deliverAction方法呼叫了action.complete(result, from),將請求到的圖片顯示到了控制元件上。
相關文章
- ThinkPHP6 原始碼分析之請求處理PHP原始碼
- 【Zookeeper】原始碼分析之請求處理鏈(一)原始碼
- Picasso原始碼分析(五):into方法追本溯源和責任鏈模式建立BitmapHunter原始碼模式
- 封裝springmvc處理ajax請求結果封裝SpringMVC
- Kafka原始碼分析(四) - Server端-請求處理框架Kafka原始碼Server框架
- SpringMVC原始碼分析:POST請求中的檔案處理SpringMVC原始碼
- Scrapy原始碼閱讀分析_4_請求處理流程原始碼
- 【Zookeeper】原始碼分析之請求處理鏈(三)之SyncRequestProcessor原始碼
- 【Zookeeper】原始碼分析之請求處理鏈(二)之PrepRequestProcessor原始碼
- springmvc原始碼 ---DispatcherServlet 處理請求SpringMVC原始碼Servlet
- 【Zookeeper】原始碼分析之請求處理鏈(四)之FinalRequestProcessor原始碼
- zookeeper原始碼 — 五、處理寫請求過程原始碼
- Picasso原始碼分析(四):不變模式、建造者模式和Request的預處理原始碼模式
- Kettle通過Http post請求webservice介面以及結果解析處理HTTPWeb
- SpringMVC請求處理過程原始碼簡析SpringMVC原始碼
- tomcat原始碼分析(第四篇 tomcat請求處理原理解析--Container原始碼分析)Tomcat原始碼AI
- Laravel 請求類原始碼分析Laravel原始碼
- Okhttp同步請求原始碼分析HTTP原始碼
- 原始碼分析Retrofit請求流程原始碼
- 直播帶貨原始碼,非同步處理中會處理兩次請求原始碼非同步
- Architecture(3)Picasso原始碼分析原始碼
- yai 請求預處理指令碼AI指令碼
- OkHttp 原始碼分析(一)—— 請求流程HTTP原始碼
- 原始碼分析Gateway請求轉發原始碼Gateway
- SpringMVC請求流程原始碼分析SpringMVC原始碼
- axios原始碼分析——取消請求iOS原始碼
- axios原始碼分析——請求流程iOS原始碼
- 死磕Spring原始碼-MVC處理HTTP分發請求Spring原始碼MVCHTTP
- 【原始碼分析】- 在SpringBoot中你會使用REST風格處理請求嗎?原始碼Spring BootREST
- KafkaClient介面與Kafka處理請求的若干特性Kafkaclient
- Spring MVC 處理一個請求的流程分析SpringMVC
- Django(48)drf請求模組原始碼分析Django原始碼
- Spring MVC原始碼(二) ----- DispatcherServlet 請求處理流程 面試必問SpringMVC原始碼Servlet面試
- 圖片載入框架Picasso - 原始碼分析框架原始碼
- 圖片載入框架Picasso原始碼分析框架原始碼
- 請求資料處理
- wordpress 處理 ajax 請求
- Mongodb請求處理流程MongoDB