Picasso源码梳理

添加依赖

1
implementation "com.squareup.picasso:picasso:2.4.0"

简单使用

1
2
3
Picasso.with(this)
.load("http://****.jpg")
.into(mImageView);

初始化梳理

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
public static Picasso with(Context context) {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder(context).build();
}
}
}
return singleton;
}

public Picasso build() {
Context context = this.context;

//初始化图片下载器
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
}
//初始化Lru缓存 使用LinkedHashMap实现
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, indicatorsEnabled, loggingEnabled);
}
}

Load流程梳理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public RequestCreator load(String path) {
if (path == null) {
return new RequestCreator(this, null, 0);
}
if (path.trim().length() == 0) {
throw new IllegalArgumentException("Path must not be empty.");
}
//将String转化为Uri对象
return load(Uri.parse(path));
}

//返回一个请求创造对象
public RequestCreator load(Uri uri) {
return new RequestCreator(this, uri, 0);
}

init流程梳理

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public void into(ImageView target, Callback callback) {
long started = System.nanoTime();
//在主线程调用直接拋出异常
checkMain();

//判断View是否存在
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) {
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
picasso.defer(target, new DeferredRequestCreator(this, target, callback));
return;
}
data.resize(width, height);
}

//生成请求对象与Key作为唯一标识
Request request = createRequest(started);
String requestKey = createKey(request);

//从内存Cache中找一遍
if (!skipMemoryCache) {
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 action =
new ImageViewAction(picasso, target, request, skipMemoryCache, noFade, errorResId,
errorDrawable, requestKey, tag, callback);

picasso.enqueueAndSubmit(action);
}

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

多类型资源处理流程

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//有下面这些资源的处理能力 他们回依次执行
List<RequestHandler> allRequestHandlers =
new ArrayList<RequestHandler>(builtInHandlers + extraCount);

// ResourceRequestHandler needs to be the first in the list to avoid
// forcing other RequestHandlers to perform null checks on request.uri
// to cover the (request.resourceId != 0) case.
allRequestHandlers.add(new ResourceRequestHandler(context));
if (extraRequestHandlers != null) {
//客户端自己实现的RequestHandlers 默认为空
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);


//依次去上边几个RequestHandler中去请求
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);
}
}

//ResourceRequestHandler 本地资源文件 判断scheme
@Override
public boolean canHandleRequest(Request data) {
if (data.resourceId != 0) {
return true;
}

return SCHEME_ANDROID_RESOURCE.equals(data.uri.getScheme());
}

//ContactsPhotoRequestHandler 联系人
@Override
public boolean canHandleRequest(Request data) {
final Uri uri = data.uri;
return (SCHEME_CONTENT.equals(uri.getScheme())
&& ContactsContract.Contacts.CONTENT_URI.getHost().equals(uri.getHost())
&& !uri.getPathSegments().contains(ContactsContract.Contacts.Photo.CONTENT_DIRECTORY));
}

//MediaStoreRequestHandler 媒体库
@Override
public boolean canHandleRequest(Request data) {
final Uri uri = data.uri;
return (SCHEME_CONTENT.equals(uri.getScheme())
&& MediaStore.AUTHORITY.equals(uri.getAuthority()));
}

//ContentStreamRequestHandler Content
@Override
public boolean canHandleRequest(Request data) {
return SCHEME_CONTENT.equals(data.uri.getScheme());
}

//AssetRequestHandler assets中资源
@Override
public boolean canHandleRequest(Request data) {
Uri uri = data.uri;
return (SCHEME_FILE.equals(uri.getScheme())
&& !uri.getPathSegments().isEmpty() && ANDROID_ASSET.equals(uri.getPathSegments().get(0)));
}

//FileRequestHandler 文件资源
@Override
public boolean canHandleRequest(Request data) {
return SCHEME_FILE.equals(data.uri.getScheme());
}

//NetworkRequestHandler 最后一个网络资源
@Override
public boolean canHandleRequest(Request data) {
String scheme = data.uri.getScheme();
return (SCHEME_HTTP.equals(scheme) || SCHEME_HTTPS.equals(scheme));
}

网络请求流程梳理

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
60
61
62
63
@Override 
public Result load(Request data) throws IOException {
//加入到下载器中 支持Okhttp和UrlConnection 有OKhttp优先使用Okhttp
//Picsso没有自己实现硬盘缓存 他的硬盘缓存通过Http的头文件来控制的
//extraRequestHandlers 可以自己实现
Response response = downloader.load(data.uri, data.loadFromLocalCacheOnly);
if (response == null) {
return null;
}

Picasso.LoadedFrom loadedFrom = response.cached ? DISK : NETWORK;

Bitmap bitmap = response.getBitmap();
if (bitmap != null) {
return new Result(bitmap, loadedFrom);
}

InputStream is = response.getInputStream();
if (is == null) {
return null;
}
// Sometimes response content length is zero when requests are being replayed. Haven't found
// root cause to this but retrying the request seems safe to do so.
if (response.getContentLength() == 0) {
Utils.closeQuietly(is);
throw new IOException("Received response with 0 content-length header.");
}
if (loadedFrom == NETWORK && response.getContentLength() > 0) {
stats.dispatchDownloadFinished(response.getContentLength());
}
try {
return new Result(decodeStream(is, data), loadedFrom);
} finally {
Utils.closeQuietly(is);
}
}

//OkHttp网络请求流程
@Override
public Response load(Uri uri, boolean localCacheOnly) throws IOException {
HttpURLConnection connection = openConnection(uri);
connection.setUseCaches(true);
//网络层的缓存
if (localCacheOnly) {
connection.setRequestProperty("Cache-Control", "only-if-cached,max-age=" + Integer.MAX_VALUE);
}

int responseCode = connection.getResponseCode();
if (responseCode >= 300) {
connection.disconnect();
throw new ResponseException(responseCode + " " + connection.getResponseMessage());
}

String responseSource = connection.getHeaderField(RESPONSE_SOURCE_OKHTTP);
if (responseSource == null) {
responseSource = connection.getHeaderField(RESPONSE_SOURCE_ANDROID);
}

long contentLength = connection.getHeaderFieldInt("Content-Length", -1);
boolean fromCache = parseResponseSourceHeader(responseSource);

return new Response(connection.getInputStream(), fromCache, contentLength);
}