Picasso是Square公司出品的一款非常優(yōu)秀的圖片加載庫(kù),它可以幫我們完成一些android中處理的圖片劈猿,使用最小的內(nèi)存來(lái)完成圖片的過(guò)渡潮孽。
使用的方法如下:
Picasso.with(context).load(“image url”).into(imageView);
源碼剖析
我們就根據(jù)圖片顯示的這一條流程下來(lái),一步步探究仗颈。
先看上面一行代碼
public static Picasso with(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("context == null");
}
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder(context).build();
}
}
}
return singleton;
}
在這里使用用了懶漢式創(chuàng)建了個(gè)實(shí)例椎例,接著用new Builder(Context).build
初始化了Picasso,主要作用為提供自定義線程池请祖、緩存脖祈、下載器等方法盖高。
有了這個(gè)實(shí)例后,會(huì)接下來(lái)調(diào)用load函數(shù)
其中Picasso重載了幾個(gè)不同的方法或舞,來(lái)適應(yīng)不同場(chǎng)合下加載圖片蒙幻。
public RequestCreator load(@Nullable Uri uri) {
.....
}
public RequestCreator load(@Nullable String path) {
.....
}
public RequestCreator load(@NonNull File file) {
.....
}
可以看到通過(guò)load會(huì)返回一個(gè)RequestCreator對(duì)象
,這個(gè)類(lèi)是用來(lái)配置加載參數(shù)的邮破,包括了placeHolder于error圖片,加載圖片的大小/旋轉(zhuǎn)/居中等屬性矫渔。
最后會(huì)調(diào)用into函數(shù)摧莽,將加載到的圖片賦給一個(gè)ImageView控件。大部分實(shí)際工作都是在into里完成的
public void into(ImageView target) {
into(target, null);
}
public void into(ImageView target, Callback callback) {
long started = System.nanoTime();
checkMain();
if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}
if (!data.hasImage()) {
picasso.cancelRequest(target);
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
return;
}
if (deferred) {
if (data.hasSize()) {
throw new IllegalStateException("Fit cannot be used with resize.");
}
int width = target.getWidth();
int height = target.getHeight();
if (width == 0 || height == 0 || target.isLayoutRequested()) {
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
picasso.defer(target, new DeferredRequestCreator(this, target, callback));
return;
}
data.resize(width, height);
}
Request request = createRequest(started);
String requestKey = createKey(request);
if (shouldReadFromMemoryCache(memoryPolicy)) {
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
if (bitmap != null) {
picasso.cancelRequest(target);
setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
if (picasso.loggingEnabled) {
log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
}
if (callback != null) {
callback.onSuccess();
}
return;
}
}
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
Action action =
new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
errorDrawable, requestKey, tag, callback, noFade);
picasso.enqueueAndSubmit(action);
}
- 會(huì)檢查是否在主線程上運(yùn)行
- 如果沒(méi)有一個(gè)圖片資源的話并且有設(shè)置placeHolder,那么就會(huì)把我們?cè)O(shè)置的placeholder顯示出來(lái)征懈,并中斷執(zhí)行
- 接下來(lái)就是創(chuàng)建了一個(gè)Request對(duì)象,我們?cè)谇懊孀龅靡恍┰O(shè)置都會(huì)被封裝到這個(gè)Request對(duì)象里面鬼悠。
- 之后由
stableKey
亏娜、uri
、rotationDegrees
袜啃、resize
幸缕、centerCrop
晰韵、``centerInside熟妓、
transformations`組成key - 檢查我們要顯示的圖片是否可以直接在緩存中獲取起愈,如果有就直接顯示出來(lái)好了
- 如果都沒(méi)有最后通過(guò)一個(gè)Action,采用異步下載顯示圖片
為了保證圖片不會(huì)錯(cuò)位,Picasso維護(hù)了Map<ImageView,Action>抬虽,每個(gè)ImageView均只對(duì)應(yīng)一個(gè)Action阐污。
若獲取的圖片Action與ImageView不符合,則丟棄笛辟,等待正確的Action執(zhí)行完手幢。
了解完P(guān)icasso加載的圖片的過(guò)程后,我們要深入到Picasso里面围来,查看圖片加載的具體實(shí)現(xiàn)方式:
之前了解到初始化Picasso监透,調(diào)用了new Builder(context).build()
方法
public Picasso build() {
Context context = this.context;
if (downloader == null) {
downloader = new OkHttp3Downloader(context);
}
if (cache == null) {
cache = new LruCache(context);
}
if (service == null) {
service = new PicassoExecutorService();
}
if (transformer == null) {
transformer = RequestTransformer.IDENTITY;
}
Stats stats = new Stats(cache);
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
}
- 重點(diǎn)看Dispatcher,在這里起到了一個(gè)調(diào)度器的作用,圖片要不要開(kāi)始下載及下載后Bitmap的返回都是通過(guò)這個(gè)調(diào)度器來(lái)執(zhí)行的牛曹。
void enqueueAndSubmit(Action action) {
Object target = action.getTarget();
if (target != null && targetToAction.get(target) != action) {
// This will also check we are on the main thread.
cancelExistingRequest(target);
targetToAction.put(target, action);
}
submit(action);
}
void submit(Action action) {
dispatcher.dispatchSubmit(action);
}
通過(guò)上面的分析我們知道醇滥,RequestCreator在into方法的最后會(huì)創(chuàng)建一個(gè)Action實(shí)例黎比,然后調(diào)用Picasso的enqueueAndSubmit方法鸳玩,而最終是調(diào)用了Dispatcher的dispatchSubmit方法不跟,也就是我們前面說(shuō)的,Dispatcher起到了調(diào)度器的作用。在Dispatcher內(nèi)部吕座,Dispatcher定義了DispatcherThread和DispatcherHandler兩個(gè)內(nèi)部類(lèi)瘪板,并在Dispatcher的構(gòu)造函數(shù)中對(duì)他們經(jīng)行了實(shí)例化,所有的調(diào)度也都是通過(guò)handler異步的執(zhí)行的锣枝。
Picasso維護(hù)了Map<ImageView,Action>兰英,每個(gè)ImageView均只對(duì)應(yīng)一個(gè)Action。
看一下Dispatcher內(nèi)部實(shí)現(xiàn)
void dispatchSubmit(Action action) {
handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
}
通過(guò)Handler最終調(diào)用了一個(gè)performSubmit()函數(shù)
void performSubmit(Action action, boolean dismissFailed) {
if (pausedTags.contains(action.getTag())) {
pausedActions.put(action.getTarget(), action);
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_PAUSED, action.request.logId(),
"because tag '" + action.getTag() + "' is paused");
}
return;
}
BitmapHunter hunter = hunterMap.get(action.getKey());
if (hunter != null) {
hunter.attach(action);
return;
}
if (service.isShutdown()) {
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
}
return;
}
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
hunter.future = service.submit(hunter);
hunterMap.put(action.getKey(), hunter);
if (dismissFailed) {
failedActions.remove(action.getTarget());
}
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
}
}
在這里獲得了一個(gè)BitmapHunter ,這其實(shí)是個(gè)Runnable的一個(gè)實(shí)現(xiàn)家制,實(shí)例最終交給另外線程池來(lái)處理泡一。
那最后圖片是怎么呈現(xiàn)出來(lái)的呢鼻忠?進(jìn)到BitmapHunter ,看run()實(shí)現(xiàn)的方法帖蔓。
@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 (NetworkRequestHandler.ResponseException e) {
if (!NetworkPolicy.isOfflineOnly(e.networkPolicy) || e.code != 504) {
exception = e;
}
dispatcher.dispatchFailed(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);
}
}
可以看到執(zhí)行了dispatcher.perComplete
方法塑娇,這個(gè)方法會(huì)自動(dòng)處理緩存圖片問(wèn)題.
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");
}
}
把圖片暫時(shí)放到了cache里,等空閑的時(shí)候再去處理來(lái)
private void batch(BitmapHunter hunter) {
if (hunter.isCancelled()) {
return;
}
if (hunter.result != null) {
hunter.result.prepareToDraw();
}
batch.add(hunter);
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
}
}
將操作放到了一個(gè)list數(shù)組(batch)里哨啃,做了一個(gè)延遲操作處理写妥,發(fā)送了一個(gè)HUNTER_DELAY_NEXT_BATCH
,又回到了handler處理.由于一直在非主線程上操作珍特,最后顯示圖片還是要回到主線程上來(lái)。這是接收到消息后的處理
void performBatchComplete() {
List<BitmapHunter> copy = new ArrayList<>(batch);
batch.clear();
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
logBatch(copy);
}
這個(gè)mainThreadHandler是在Dispatcher實(shí)例化時(shí)由外部傳遞進(jìn)來(lái)的,我們?cè)谇懊娴姆治鲋锌吹匠昴罚琍icasso在通過(guò)Builder創(chuàng)建時(shí)會(huì)對(duì)Dispatcher進(jìn)行實(shí)例化宋距,在那個(gè)地方將主線程的handler傳了進(jìn)來(lái),我們回到Picasso這個(gè)類(lèi)淫僻,看到其有一個(gè)靜態(tài)成員變量HANDLER壶唤,這樣我們也就清楚了。
static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
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;
}
}
};
通過(guò)Hnadler回到了Picasso里悯辙,最終調(diào)用picasso.complete
方法.
void complete(BitmapHunter hunter) {
Action single = hunter.getAction();
List<Action> joined = hunter.getActions();
boolean hasMultiple = joined != null && !joined.isEmpty();
boolean shouldDeliver = single != null || hasMultiple;
if (!shouldDeliver) {
return;
}
Uri uri = hunter.getData().uri;
Exception exception = hunter.getException();
Bitmap result = hunter.getResult();
LoadedFrom from = hunter.getLoadedFrom();
if (single != null) {
deliverAction(result, from, single, exception);
}
if (hasMultiple) {
//noinspection ForLoopReplaceableByForEach
for (int i = 0, n = joined.size(); i < n; i++) {
Action join = joined.get(i);
deliverAction(result, from, join, exception);
}
}
if (listener != null && exception != null) {
listener.onImageLoadFailed(this, uri, exception);
}
}
Picasso使用了ImageViewAction來(lái)進(jìn)行處理躲撰,也就是在ImageViewAction中的complete方法完成了最后的圖片渲染工作击费。
@Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
if (result == null) {
throw new AssertionError(
String.format("Attempted to complete action with no result!\n%s", this));
}
ImageView target = this.target.get();
if (target == null) {
return;
}
Context context = picasso.context;
boolean indicatorsEnabled = picasso.indicatorsEnabled;
PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
if (callback != null) {
callback.onSuccess();
}
}
到這里就把這個(gè)圖片的渲染過(guò)程講完了
參考: