一 介紹
Square公司開源的圖片加載庫。優(yōu)點(diǎn)是功能還算完善,能滿足基本的圖片加載需求,使用簡(jiǎn)單,體量輕易推倒。
官方鏈接:http://square.github.io/picasso/
Git: https://github.com/square/picasso
二 分析
1. 簡(jiǎn)單調(diào)用
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
2. 流程圖

3. 核心類分析
(主流程順序,以下貼出的代碼都是經(jīng)過篩選了比較重要的代碼)
①Picasso
-- 單例,構(gòu)造ExecutorService線程池(默認(rèn)3個(gè)線程),Dispatcher,LruCache,Downloader(調(diào)用okhttp),其他基本沒干什么正事。
//Builder模式構(gòu)建Picasso實(shí)例
public static class Builder {
private final Context context;
private Downloader downloader;
private ExecutorService service;
private Cache cache;
private Listener listener;
private RequestTransformer transformer;
private List<RequestHandler> requestHandlers;
private Bitmap.Config defaultBitmapConfig;
private boolean indicatorsEnabled;
private boolean loggingEnabled;
/** Start building a new {@link Picasso} instance. */
public Builder(Context context) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null.");
}
this.context = context.getApplicationContext();
}
....//省略以下代碼
}
②RequestCreator
-- 主流程調(diào)用的方法into(), 里面做的最重要的兩件事:1.創(chuàng)建Request, 2.把Action加到Dispatcher隊(duì)列。(Request, Action, Dispatcher幾個(gè)概念后面會(huì)做說明)
public void into(ImageView target, Callback callback) {
Request request = createRequest(started);
String requestKey = createKey(request);
//查看內(nèi)存緩存是否已經(jīng)有了,有的話直接set,不用再去構(gòu)造Action獲取了
if (shouldReadFromMemoryCache(memoryPolicy)) {
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
if (bitmap != null) {
picasso.cancelRequest(target);
setBitmap(target, picasso.context, bitmap, MEMORY, noFade, picasso.indicatorsEnabled);
if (callback != null) {
callback.onSuccess();
}
return;
}
}
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
//構(gòu)造Action
Action action =
new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
errorDrawable, requestKey, tag, callback, noFade);
//扔給任務(wù)調(diào)度器
picasso.enqueueAndSubmit(action);
}
③Request
-- 請(qǐng)求model,存儲(chǔ)請(qǐng)求相關(guān)的數(shù)據(jù),包括Transformation list,部分內(nèi)置的轉(zhuǎn)換屬性,Bitmap壓縮屬性, 任務(wù)優(yōu)先級(jí), 圖片資源的相關(guān)信息等。
//類結(jié)構(gòu),原生注釋很詳細(xì)
public final class Request {
private static final long TOO_LONG_LOG = TimeUnit.SECONDS.toNanos(5);
/** A unique ID for the request. */
int id;
/** The time that the request was first submitted (in nanos). */
long started;
/** The {@link NetworkPolicy} to use for this request. */
int networkPolicy;
/**
* The image URI.
* <p>
* This is mutually exclusive with {@link #resourceId}.
*/
public final Uri uri;
/**
* The image resource ID.
* <p>
* This is mutually exclusive with {@link #uri}.
*/
public final int resourceId;
/**
* Optional stable key for this request to be used instead of the URI or resource ID when
* caching. Two requests with the same value are considered to be for the same resource.
*/
public final String stableKey;
/** List of custom transformations to be applied after the built-in transformations. */
public final List<Transformation> transformations;
/** Target image width for resizing. */
public final int targetWidth;
/** Target image height for resizing. */
public final int targetHeight;
/**
* True if the final image should use the 'centerCrop' scale technique.
* <p>
* This is mutually exclusive with {@link #centerInside}.
*/
public final boolean centerCrop;
/**
* True if the final image should use the 'centerInside' scale technique.
* <p>
* This is mutually exclusive with {@link #centerCrop}.
*/
public final boolean centerInside;
public final boolean onlyScaleDown;
/** Amount to rotate the image in degrees. */
public final float rotationDegrees;
/** Rotation pivot on the X axis. */
public final float rotationPivotX;
/** Rotation pivot on the Y axis. */
public final float rotationPivotY;
/** Whether or not {@link #rotationPivotX} and {@link #rotationPivotY} are set. */
public final boolean hasRotationPivot;
/** True if image should be decoded with inPurgeable and inInputShareable. */
public final boolean purgeable;
/** Target image config for decoding. */
public final Bitmap.Config config;
/** The priority of this request. */
public final Priority priority;
....//省略以下代碼
④Action
-- 可以看做單個(gè)圖片加載的任務(wù)model,抽象類。
實(shí)現(xiàn)類可見下圖,常用的是ImageViewAction,它會(huì)在抽象方法complete真正實(shí)現(xiàn)把Bitmap給ImageView的行為。

abstract class Action<T> {
final Picasso picasso; //picasso單例
final Request request; //當(dāng)前任務(wù)的request
final WeakReference<T> target; //目標(biāo),imageview
final boolean noFade; //是否漸進(jìn)漸出
final int memoryPolicy; //內(nèi)存緩存策略
final int networkPolicy; //網(wǎng)絡(luò)緩存策略
final int errorResId; //錯(cuò)誤占位圖資源id
final Drawable errorDrawable; //錯(cuò)誤占位圖資源drawable
final String key; //內(nèi)存緩存的key
final Object tag; //dispatcher的任務(wù)標(biāo)識(shí)
boolean willReplay; //是否再次放到任務(wù)隊(duì)列中
boolean cancelled; //是否取消
//Bitmap加載成功回調(diào)
abstract void complete(Bitmap result, Picasso.LoadedFrom from);
//加載失敗回調(diào)
abstract void error();
....//省略以下代碼
⑤Dispatcher
-- 看名字就看出來是個(gè)任務(wù)分發(fā)調(diào)度器,
dispatcher方法流程:

class Dispatcher {
final DispatcherThread dispatcherThread; //dispatcher線程,主要作用是獲取looper給handler用
final Context context;
final ExecutorService service; //picasso創(chuàng)建的線程池,唯一
final Downloader downloader; //下載器,唯一
final Map<String, BitmapHunter> hunterMap; //所有的BitmapHunter map
final Map<Object, Action> failedActions;
final Map<Object, Action> pausedActions;
final Set<Object> pausedTags;
final Handler handler; //dispatcher線程handler,任務(wù)調(diào)用使用
final Handler mainThreadHandler; //主線程handler,用于complete后的行為
final Cache cache; //內(nèi)存緩存
final Stats stats;
final List<BitmapHunter> batch; //批量complete狀態(tài)的BitmapHunter
final NetworkBroadcastReceiver receiver; //監(jiān)控網(wǎng)絡(luò)狀態(tài),用于網(wǎng)絡(luò)緩存策略
//從Action構(gòu)造BitmapHunter并且放到線程池中
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;
}
//構(gòu)造request
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
//執(zhí)行hunter
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è)任務(wù)完成 ,batch聚合成功任務(wù)結(jié)果
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");
}
}
//給picasso主線程處理批量結(jié)果
void performBatchComplete() {
List<BitmapHunter> copy = new ArrayList<BitmapHunter>(batch);
batch.clear();
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
logBatch(copy);
}
⑥BitmapHunter
-- 比較簡(jiǎn)單,實(shí)現(xiàn)了Runnable接口, 重點(diǎn)關(guān)注hunt()方法。
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
//省略從內(nèi)存讀bitmap代碼...
//從requestHandler 同步load result,RequestHandler見后面分析
RequestHandler.Result result = requestHandler.load(data, networkPolicy);
if (result != null) {
bitmap = result.getBitmap();
// If there was no Bitmap then we need to decode it from the stream.
if (bitmap == null) {
InputStream is = result.getStream();
bitmap = decodeStream(is, data);
}
}
if (bitmap != null) {
//如果需要做內(nèi)置的轉(zhuǎn)換,則轉(zhuǎn)出相應(yīng)的bitmap
if (data.needsTransformation() || exifOrientation != 0) {
bitmap = transformResult(data, bitmap, exifOrientation);
}
//如果需要做自定義的轉(zhuǎn)換,則轉(zhuǎn)出相應(yīng)的bitmap
if (data.hasCustomTransformations()) {
bitmap = applyCustomTransformations(data.transformations, bitmap);
}
}
}
}
}
return bitmap;
}
⑦RequestHandler
-- 抽象類,抽象方法load() 方法獲取Result, Result里有bitmap和inputstream
實(shí)現(xiàn)類:

我們來看下最常用的NetworkRequestHandler
class NetworkRequestHandler extends RequestHandler {
//根據(jù)request加載出bitmap或者inputstream
@Override public Result load(Request request, int networkPolicy) throws IOException {
//走Downloader請(qǐng)求,downloader內(nèi)部是okhttp實(shí)現(xiàn)
Response response = downloader.load(request.uri, request.networkPolicy);
Bitmap bitmap = response.getBitmap();
if (bitmap != null) {
return new Result(bitmap, loadedFrom);
}
InputStream is = response.getInputStream();
if (is == null) {
return null;
}
}
BitmapHunter里L(fēng)ist<RequestHandler>是在picasso的構(gòu)造方法中注入的。
Picasso類相關(guān)代碼:
List<RequestHandler> allRequestHandlers =
new ArrayList<RequestHandler>(builtInHandlers + extraCount);
allRequestHandlers.add(new ResourceRequestHandler(context));
if (extraRequestHandlers != null) {
allRequestHandlers.addAll(extraRequestHandlers);
}
allRequestHandlers.add(new ContactsPhotoRequestHandler(context));
allRequestHandlers.add(new MediaStoreRequestHandler(context));
allRequestHandlers.add(new ContentStreamRequestHandler(context));
allRequestHandlers.add(new AssetRequestHandler(context));
allRequestHandlers.add(new FileRequestHandler(context));
allRequestHandlers.add(new NetworkRequestHandler(dispatcher.downloader, stats));
requestHandlers = Collections.unmodifiableList(allRequestHandlers);
BitmapHunter類相關(guān)代碼:
List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
for (int i = 0, count = requestHandlers.size(); i < count; i++) {
RequestHandler requestHandler = requestHandlers.get(i);
//責(zé)任鏈模式,第一個(gè)可用的handler直接使用
if (requestHandler.canHandleRequest(request)) {
return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
}
}
三 內(nèi)部機(jī)制及優(yōu)缺點(diǎn)
1、提供了雙緩存,Memory緩存使用LinkedHashMap實(shí)現(xiàn),提供命中率統(tǒng)計(jì)。
Disk緩存使用的是okhttp內(nèi)部的,無法設(shè)置其他的Disk緩存,要想實(shí)現(xiàn)只能自己改造代碼了。(OkHttpDownloader和OkHttp3Downloader)
2、自定制Bitmap轉(zhuǎn)換,需要繼承Transformation接口,在構(gòu)造RequestCreator的時(shí)候add進(jìn)去。類似切圓角,高斯模糊等需求都在這里完成。Picasso本身提供了Resize,CenterCrop,Rotate,CenterInside轉(zhuǎn)換方式。
3、圖片渲染只提供了fade方式, 見PicassoDrawable。
4、可load的圖片資源種類:Resource,Asset, File, Http, (Https之前版本不支持,從最新依賴的okhttp 3.0.1看應(yīng)該是支持的,未測(cè)試過)
5、不支持Gif, 支持webp。