Picasso源码解析

1 功能介绍

Picasso是square推出的一个Android平台的图片加载开源库。
包含内存缓存和磁盘缓存两级缓存。
可定制度高、自定义配置、提供接口自定义处理图片等。
在 Adapter 中自动处理 ImageView 的缓存并且取消之前的图片下载任务。

2.总体设计

总体设计图

###3 详细设计

3.1 加载流程图及类图

流程图:
图片加载流程图

类图:
图片加载流程图

3.2 核心类分析

3.2.1 Picasso.Builder

这是一个通过Build创建Picasso的工具类。

3.2.2 Picasso.java

Picasso保存了所有请求的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private final Listener listener;
private final RequestTransformer requestTransformer;
private final CleanupThread cleanupThread;
private final List<RequestHandler> requestHandlers;

final Context context;
final Dispatcher dispatcher;
final Cache cache;
final Stats stats;
final Map<Object, Action> targetToAction;
final Map<ImageView, DeferredRequestCreator> targetToDeferredRequestCreator;
final ReferenceQueue<Object> referenceQueue;
final Bitmap.Config defaultBitmapConfig;

Picasso(Context context, Dispatcher dispatcher, Cache cache, Listener listener,
RequestTransformer requestTransformer, List<RequestHandler> extraRequestHandlers, Stats stats,
Bitmap.Config defaultBitmapConfig, boolean indicatorsEnabled, boolean loggingEnabled)

//如果没有初始化,使用默认初始化Picasso
public static Picasso with(Context context)

//创建RequestCreator
public RequestCreator load(...)

这里会检查当前ImageView是否已经存在加载任务,如果存在直接取消任务,开始新的任务。//这样防止了ListView中的ImageView复用问题。

1
2
3
4
5
6
7
8
9
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);
}

3.2.3 Downloader.java

图片下载客户端。默认为OkHttpDownloader,okhttp存在DiskLruCache数据缓存,如果使用其他的下载客户端(HttpURLConnection、HttpClient)需要自己定义数据本地缓存。

1
Response load(Uri uri, int networkPolicy) throws IOException;

3.2.4 Cache.java

运行内存图片缓存。默认使用的缓存是LruCache,基本原理就是向缓存中添加图片,计算图片的大小,计算缓存所有图片的总大小,如果总图片的大小超过设定的大小,就删除使用频率低的图片。

1
2
3
4
5
//从缓存中获取图片
Bitmap get(String key);

//向缓存中添加图片
void set(String key, Bitmap bitmap)

3.2.5 Dispatcher.java

请求分发和请求接口回调。通过命名可以清晰的知道其含义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void dispatchSubmit(Action action) 

void dispatchCancel(Action action)

void dispatchPauseTag(Object tag)

void dispatchResumeTag(Object tag)

void dispatchComplete(BitmapHunter hunter)

void dispatchRetry(BitmapHunter hunter)

void dispatchFailed(BitmapHunter hunter)

void dispatchNetworkStateChange(NetworkInfo info)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void performSubmit(Action action, boolean dismissFailed) {
if (pausedTags.contains(action.getTag())) {
pausedActions.put(action.getTarget(), action);
return;
}

BitmapHunter hunter = hunterMap.get(action.getKey());
if (hunter != null) {
hunter.attach(action);
return;
}

if (service.isShutdown()) {

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());
}
}

(1) 判断当前tag的请求是已经暂停,如果已经暂停添加到暂停队列中。
(2) BitmapHunter其实实现Runnable,是图片请求任务,判断图片资源的请求是否存在,如果存在将action保存到BitmapHunter中,等图片请求完成后一起处理。
(3)创建BitmapHunter,请求图片,保存到hunterMap中。

1
2
3
4
5
6
7
8
9
10
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");
}
}

请求完成以后将结果添加到缓存中,hunterMap清除当前hunter。

3.2.6 RequestHandler.java

请求处理。包括网络请求,本地文件请求等。默认有6个实现类:
ContactsPhotoRequestHandler(context);
MediaStoreRequestHandler(context);
ContentStreamRequestHandler(context);
AssetRequestHandler(context);
FileRequestHandler(context);
NetworkRequestHandler(dispatcher.downloader, stats);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static void calculateInSampleSize(int reqWidth, int reqHeight, int width, int height,
BitmapFactory.Options options, Request request) {
int sampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int heightRatio;
final int widthRatio;
if (reqHeight == 0) {
sampleSize = (int) Math.floor((float) width / (float) reqWidth);
} else if (reqWidth == 0) {
sampleSize = (int) Math.floor((float) height / (float) reqHeight);
} else {
heightRatio = (int) Math.floor((float) height / (float) reqHeight);
widthRatio = (int) Math.floor((float) width / (float) reqWidth);
sampleSize = request.centerInside
? Math.max(heightRatio, widthRatio)
: Math.min(heightRatio, widthRatio);
}
}
options.inSampleSize = sampleSize;
options.inJustDecodeBounds = false;
}

基本的图片压缩算法,算出压缩比,通过BitmapFactory压缩图片,但是这样的压缩不够全面,只有整数倍压缩,所以压缩过后还需要进行二次压缩,后面将出现二次压缩的算法。

1
2
3
4
//获取图片资源,将数据保存在Result中。
public abstract Result load(Request request, int networkPolicy) throws IOException;
//根据Request,判断当前RequestHandler是否可以处理。
public abstract boolean canHandleRequest(Request data);
3.2.7 RequestCreator.java
1
2
//下面方法中设置都会赋值在data中,然后根据data中配置的值创建图片请求
private final Request.Builder data;

图片压缩完成以后,取中间的那一块,如下图。

1
public RequestCreator centerCrop()

centerCrop

图片会按设置的尺寸,根据宽高比例压缩。与centerCrop不能同时使用

1
public RequestCreator centerInside()

centerCrop

根据ImageView的大小设置图片压缩的大小

1
public RequestCreator fit()

直接设置图片压缩的大小,与fit不可同时使用

1
public RequestCreator resize

设置压缩后的图片自定义处理接口

1
public RequestCreator transform(Transformation transformation)

设置请求前的图片

1
public RequestCreator placeholder()

设置请求错误的图片

1
public RequestCreator error()

将图片显示在设置的View上面

1
2
3
4
//直接给ImageView设置bitmap, 也会使用回调Callback,但是没用传递数据。
public void into(ImageView target, Callback callback)
//Target 相当于一个CallBack,当图片请求成功以后,将Bitmap回调。
public void into(Target target)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public void into(ImageView target, Callback callback) {
long started = System.nanoTime();
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) {
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);
}

(1)deferred表示自动获取ImageView的大小,但是由于ImageView设置wrap_content的时候是获取不到ImageView的大小,所以添加到延迟加载的队列中,当ImageView显示到屏幕以后会开启任务。如果xml中固定了ImageView的大小直接将大小获取,保存起来。
(2)创建Request,根据Request获取缓存数据的key,判断缓存是否存在,存在直接返回。
(3)如果缓存不存在,创建ImageViewAction,提交Action加载图片。

3.2.8 Action.java
1
2
3
4
5
final Picasso picasso;
final Request request;
final WeakReference<T> target;
final String key;
final Object tag;
3.2.9 BitmapHunter.java

Bitmap请求Runnable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Override public void run() {
try {
updateThreadName(data);
result = hunt();
if (result == null) {
dispatcher.dispatchFailed(this);
} else {
dispatcher.dispatchComplete(this);
}
} catch (Downloader.ResponseException e) {
dispatcher.dispatchFailed(this);
} catch (NetworkRequestHandler.ContentLengthException e) {
dispatcher.dispatchRetry(this);
} catch (IOException 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) {
dispatcher.dispatchFailed(this);
} finally {
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
Bitmap hunt() throws IOException {
Bitmap bitmap = null;

if (shouldReadFromMemoryCache(memoryPolicy)) {
bitmap = cache.get(key);
if (bitmap != null) {
stats.dispatchCacheHit();
loadedFrom = MEMORY;
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;
}

这个方法是处理图片最核心的方法,包括请求图片、压缩图片、处理图片。
(1)首先还是会校验内存缓存中是否已经存在。
(2)requestHandler很重要,是请求图片的工具类,看forRequest这个方法,首先获取所有的RequestHandler, 之前有说过默认五种RequestHandler,然后获取可以处理请求的RequestHandler。
(3)生成压缩后的bitmap (整数倍)。
(4)处理图片的转向,小数倍压缩。
(5)自定义图片处理。
(6)分发结果

1
2
3
4
5
6
7
8
//生成BitmapHunter。
static BitmapHunter forRequest();

//压缩图片
static Bitmap decodeStream(InputStream stream, Request request) throws IOException;

//压缩和处理图片
static Bitmap transformResult(Request data, Bitmap result, int exifRotation)

3.杂谈