《Android开源库》Picasso

1. 前言

最近项目开发中使用到来Picasso,并且碰到了一个部分图片无法加载的问题,使用Glide可以正常加载,使用最新的Picasso3.0.0-SNAPSHOT版本也没有问题,最后使用Picasso自带的异常监听获取堆栈,并且在Github中找到的同样的问题,具体查阅:

https://github.com/square/picasso/issues/1514

所以,想详细的看看Picasso的详细用法和master分支上的源代码。

2. 基本介绍

Picasso 是Square 公司开源的Android 端的图片加载和缓存框架。图片的加载是一个令人很头疼的问题,因为涉及的内容比较多,比如网络请求,本地存储,图片缓存,图片回收等等,处理不好可能会出现图片变形内存泄漏等令开发者焦头烂额的问题,但是Picasso帮我们做了很多事情,比如:

  • 处理Adapter 中ImageView的回收和取消下载。
  • 使用最小的内存 来做复杂的图片变换。比如高斯模糊,圆角、圆形等处理。
  • 自动帮我们缓存图片。内存和磁盘缓存

3. 基本使用

以Android Studio为开发环境,以Gradle为构建工具,其他的本文不做介绍。

3.1 添加依赖

1
compile 'com.squareup.picasso:picasso:2.5.2'

3.2 混淆

1
-dontwarn com.squareup.okhttp.**

3.3 加载图片

1
2
3
4
5
6
7
8
9
10
11
/* 网络图片 */
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

/* 图片资源 */
Picasso.with(context).load(R.drawable.landing_screen).into(imageView1);

/* 本地图片*/
Picasso.with(context).load("file:///android_asset/DvpvklR.png").into(imageView2);

/* 本地图片文件 */
Picasso.with(context).load(new File(...)).into(imageView3);

3.3.1 Picasso.with(Context context)

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
/**
* The global default {@link Picasso} instance.
* <p>
* This instance is automatically initialized with defaults that are suitable to most
* implementations.
* <ul>
* <li>LRU memory cache of 15% the available application RAM</li>
* <li>Disk cache of 2% storage space up to 50MB but no less than 5MB. (Note: this is only
* available on API 14+ <em>or</em> if you are using a standalone library that provides a disk
* cache on all API levels like OkHttp)</li>
* <li>Three download threads for disk and network access.</li>
* </ul>
* <p>
* If these settings do not meet the requirements of your application you can construct your own
* with full control over the configuration by using {@link Picasso.Builder} to create a
* {@link Picasso} instance. You can either use this directly or by setting it as the global
* instance with {@link #setSingletonInstance}.
*/
public static Picasso with(Context context) {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder(context).build();
}
}
}
return singleton;
}

构造一个默认的Picasso单例,如果注释中的一些基本配置需求无法满足你,你可以使用Picasso.Builder来构造自己的Picasso实例。

3.3.2 load()

这里写图片描述
很显然,四个load方法,不同的入参,相同类型的返回值。针对不同的图片来源,执行不同的加载方式。

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
/**
* Start an image request using the specified URI.
* <p>
* Passing {@code null} as a {@code uri} will not trigger any request but will set a placeholder,
* if one is specified.
*
* @see #load(File)
* @see #load(String)
* @see #load(int)
*/
public RequestCreator load(Uri uri) {
return new RequestCreator(this, uri, 0);
}

/**
* Start an image request using the specified path. This is a convenience method for calling
* {@link #load(Uri)}.
* <p>
* This path may be a remote URL, file resource (prefixed with {@code file:}), content resource
* (prefixed with {@code content:}), or android resource (prefixed with {@code
* android.resource:}.
* <p>
* Passing {@code null} as a {@code path} will not trigger any request but will set a
* placeholder, if one is specified.
*
* @see #load(Uri)
* @see #load(File)
* @see #load(int)
* @throws IllegalArgumentException if {@code path} is empty or blank string.
*/
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.");
}
return load(Uri.parse(path));
}

/**
* Start an image request using the specified image file. This is a convenience method for
* calling {@link #load(Uri)}.
* <p>
* Passing {@code null} as a {@code file} will not trigger any request but will set a
* placeholder, if one is specified.
* <p>
* Equivalent to calling {@link #load(Uri) load(Uri.fromFile(file))}.
*
* @see #load(Uri)
* @see #load(String)
* @see #load(int)
*/
public RequestCreator load(File file) {
if (file == null) {
return new RequestCreator(this, null, 0);
}
return load(Uri.fromFile(file));
}

/**
* Start an image request using the specified drawable resource ID.
*
* @see #load(Uri)
* @see #load(String)
* @see #load(File)
*/
public RequestCreator load(int resourceId) {
if (resourceId == 0) {
throw new IllegalArgumentException("Resource ID must not be zero.");
}
return new RequestCreator(this, null, resourceId);
}

每一个load()方法都是创建一个RequestCreator实例,load(File file)和load(String path)最后都是通过转换成load(Uri uri)的方式实现加载。而RequestCreator的构造方法中三个参数,第一个是Picasso实例,一个是uri,一个是resourceId,通过后两个参数覆盖所有的加载场景。

1
2
3
4
5
6
7
8
RequestCreator(Picasso picasso, Uri uri, int resourceId) {
if (picasso.shutdown) {
throw new IllegalStateException(
"Picasso instance already shut down. Cannot submit new requests.");
}
this.picasso = picasso;
this.data = new Request.Builder(uri, resourceId, picasso.defaultBitmapConfig);
}

3.3.3 into()

RequestCreator中很多方法都是我们经常会用到的。先看into()方法
这里写图片描述

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/**
* Asynchronously fulfills the request into the specified {@link Target}. In most cases, you
* should use this when you are dealing with a custom {@link android.view.View View} or view
* holder which should implement the {@link Target} interface.
* <p>
* Implementing on a {@link android.view.View View}:
* <blockquote><pre>
* public class ProfileView extends FrameLayout implements Target {
* {@literal @}Override public void onBitmapLoaded(Bitmap bitmap, LoadedFrom from) {
* setBackgroundDrawable(new BitmapDrawable(bitmap));
* }
*
* {@literal @}Override public void onBitmapFailed() {
* setBackgroundResource(R.drawable.profile_error);
* }
*
* {@literal @}Override public void onPrepareLoad(Drawable placeHolderDrawable) {
* frame.setBackgroundDrawable(placeHolderDrawable);
* }
* }
* </pre></blockquote>
* Implementing on a view holder object for use inside of an adapter:
* <blockquote><pre>
* public class ViewHolder implements Target {
* public FrameLayout frame;
* public TextView name;
*
* {@literal @}Override public void onBitmapLoaded(Bitmap bitmap, LoadedFrom from) {
* frame.setBackgroundDrawable(new BitmapDrawable(bitmap));
* }
*
* {@literal @}Override public void onBitmapFailed() {
* frame.setBackgroundResource(R.drawable.profile_error);
* }
*
* {@literal @}Override public void onPrepareLoad(Drawable placeHolderDrawable) {
* frame.setBackgroundDrawable(placeHolderDrawable);
* }
* }
* </pre></blockquote>
* <p>
* <em>Note:</em> This method keeps a weak reference to the {@link Target} instance and will be
* garbage collected if you do not keep a strong reference to it. To receive callbacks when an
* image is loaded use {@link #into(android.widget.ImageView, Callback)}.
*/
public void into(Target target) {
long started = System.nanoTime();
checkMain();

if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}
if (deferred) {
throw new IllegalStateException("Fit cannot be used with a Target.");
}

if (!data.hasImage()) {
picasso.cancelRequest(target);
target.onPrepareLoad(setPlaceholder ? getPlaceholderDrawable() : null);
return;
}

Request request = createRequest(started);
String requestKey = createKey(request);

if (shouldReadFromMemoryCache(memoryPolicy)) {
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
if (bitmap != null) {
picasso.cancelRequest(target);
target.onBitmapLoaded(bitmap, MEMORY);
return;
}
}

target.onPrepareLoad(setPlaceholder ? getPlaceholderDrawable() : null);

Action action =
new TargetAction(picasso, target, request, memoryPolicy, networkPolicy, errorDrawable,
requestKey, tag, errorResId);
picasso.enqueueAndSubmit(action);
}

/**
* Asynchronously fulfills the request into the specified {@link RemoteViews} object with the
* given {@code viewId}. This is used for loading bitmaps into a {@link Notification}.
*/
public void into(RemoteViews remoteViews, int viewId, int notificationId,
Notification notification) {
long started = System.nanoTime();

if (remoteViews == null) {
throw new IllegalArgumentException("RemoteViews must not be null.");
}
if (notification == null) {
throw new IllegalArgumentException("Notification must not be null.");
}
if (deferred) {
throw new IllegalStateException("Fit cannot be used with RemoteViews.");
}
if (placeholderDrawable != null || placeholderResId != 0 || errorDrawable != null) {
throw new IllegalArgumentException(
"Cannot use placeholder or error drawables with remote views.");
}

Request request = createRequest(started);
String key = createKey(request, new StringBuilder()); // Non-main thread needs own builder.

RemoteViewsAction action =
new NotificationAction(picasso, request, remoteViews, viewId, notificationId, notification,
memoryPolicy, networkPolicy, key, tag, errorResId);

performRemoteViewInto(action);
}

/**
* Asynchronously fulfills the request into the specified {@link RemoteViews} object with the
* given {@code viewId}. This is used for loading bitmaps into all instances of a widget.
*/
public void into(RemoteViews remoteViews, int viewId, int[] appWidgetIds) {
long started = System.nanoTime();

if (remoteViews == null) {
throw new IllegalArgumentException("remoteViews must not be null.");
}
if (appWidgetIds == null) {
throw new IllegalArgumentException("appWidgetIds must not be null.");
}
if (deferred) {
throw new IllegalStateException("Fit cannot be used with remote views.");
}
if (placeholderDrawable != null || placeholderResId != 0 || errorDrawable != null) {
throw new IllegalArgumentException(
"Cannot use placeholder or error drawables with remote views.");
}

Request request = createRequest(started);
String key = createKey(request, new StringBuilder()); // Non-main thread needs own builder.

RemoteViewsAction action =
new AppWidgetAction(picasso, request, remoteViews, viewId, appWidgetIds, memoryPolicy,
networkPolicy, key, tag, errorResId);

performRemoteViewInto(action);
}

/**
* Asynchronously fulfills the request into the specified {@link ImageView}.
* <p>
* <em>Note:</em> This method keeps a weak reference to the {@link ImageView} instance and will
* automatically support object recycling.
*/
public void into(ImageView target) {
into(target, null);
}

/**
* Asynchronously fulfills the request into the specified {@link ImageView} and invokes the
* target {@link Callback} if it's not {@code null}.
* <p>
* <em>Note:</em> The {@link Callback} param is a strong reference and will prevent your
* {@link android.app.Activity} or {@link android.app.Fragment} from being garbage collected. If
* you use this method, it is <b>strongly</b> recommended you invoke an adjacent
* {@link Picasso#cancelRequest(android.widget.ImageView)} call to prevent temporary leaking.
*/
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) {
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);
}

可以看到5个into()方法,可以看到Picasso常用的是直接往ImageView,但是它的功能远远不止如此,我们可以通过实现Target接口来实现自定义的图片处理,可以用来设背景,可以用来填充布局,可扩展性很强,也支持RemoteView。而我们常用的

1
2
3
public void into(ImageView target) {
into(target, null);
}

转到有Callback的执行方法。我们可以通过使用带有回调的into方法来执行图片加载成功或者失败后的事件处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface Callback {
void onSuccess();

void onError();

public static class EmptyCallback implements Callback {

@Override public void onSuccess() {
}

@Override public void onError() {
}
}
}

看下public void into(ImageView target, Callback callback)这个方法:

  1. checkMain() 判断是否为主进程
  2. 非空检查。判断target是否为空,uri 或者 resourceId是否为空;
  3. deferred是否为true。这个和是否调用fit方法有关。由于fit方法是让图片适应ImageView的大小,所以需要ImageView大小明确之后才执行请求,所以需要延迟执行。
  4. 创建Request和requestKey
  5. 是否在Cache查找该图片,通过requestKey,这个Key的构建过程和很多内容有关系。
  6. 设置占位图片
  7. 构建Action并提交

以上就是图片加载的主要过程。

4. 进阶用法(链式使用)

主要介绍一下Picasso的其他用法

4.1 noPlaceholder()

不设置占位图片,不能于placeHolder同时使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* Explicitly opt-out to having a placeholder set when calling {@code into}.
* <p>
* By default, Picasso will either set a supplied placeholder or clear the target
* {@link ImageView} in order to ensure behavior in situations where views are recycled. This
* method will prevent that behavior and retain any already set image.
*/
public RequestCreator noPlaceholder() {
if (placeholderResId != 0) {
throw new IllegalStateException("Placeholder resource already set.");
}
if (placeholderDrawable != null) {
throw new IllegalStateException("Placeholder image already set.");
}
setPlaceholder = false;
return this;
}

4.2 placeholder()

设置占位图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* A placeholder drawable to be used while the image is being loaded. If the requested image is
* not immediately available in the memory cache then this resource will be set on the target
* {@link ImageView}.
*/
public RequestCreator placeholder(int placeholderResId) {
if (!setPlaceholder) {
throw new IllegalStateException("Already explicitly declared as no placeholder.");
}
if (placeholderResId == 0) {
throw new IllegalArgumentException("Placeholder image resource invalid.");
}
if (placeholderDrawable != null) {
throw new IllegalStateException("Placeholder image already set.");
}
this.placeholderResId = placeholderResId;
return this;
}

4.3 error()

设置加载出错时的图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/** An error drawable to be used if the request image could not be loaded. */
public RequestCreator error(int errorResId) {
if (errorResId == 0) {
throw new IllegalArgumentException("Error image resource invalid.");
}
if (errorDrawable != null) {
throw new IllegalStateException("Error image already set.");
}
this.errorResId = errorResId;
return this;
}

/** An error drawable to be used if the request image could not be loaded. */
public RequestCreator error(Drawable errorDrawable) {
if (errorDrawable == null) {
throw new IllegalArgumentException("Error image may not be null.");
}
if (errorResId != 0) {
throw new IllegalStateException("Error image already set.");
}
this.errorDrawable = errorDrawable;
return this;
}

4.4 tag()

设置tag,方便后续pause,cancel,或者resume。这个应该用的比较少,有可能通过tag来cancel一下。

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
/**
* Assign a tag to this request. Tags are an easy way to logically associate
* related requests that can be managed together e.g. paused, resumed,
* or canceled.
* <p>
* You can either use simple {@link String} tags or objects that naturally
* define the scope of your requests within your app such as a
* {@link android.content.Context}, an {@link android.app.Activity}, or a
* {@link android.app.Fragment}.
*
* <strong>WARNING:</strong>: Picasso will keep a reference to the tag for
* as long as this tag is paused and/or has active requests. Look out for
* potential leaks.
*
* @see Picasso#cancelTag(Object)
* @see Picasso#pauseTag(Object)
* @see Picasso#resumeTag(Object)
*/
public RequestCreator tag(Object tag) {
if (tag == null) {
throw new IllegalArgumentException("Tag invalid.");
}
if (this.tag != null) {
throw new IllegalStateException("Tag already set.");
}
this.tag = tag;
return this;
}

4.5 fit() & unfit()

调整图片大小来适配target的边界,这个会导致图片的加载过程在ImageView布局确定之后进行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Attempt to resize the image to fit exactly into the target {@link ImageView}'s bounds. This
* will result in delayed execution of the request until the {@link ImageView} has been laid out.
* <p>
* <em>Note:</em> This method works only when your target is an {@link ImageView}.
*/
public RequestCreator fit() {
deferred = true;
return this;
}

/** Internal use only. Used by {@link DeferredRequestCreator}. */
RequestCreator unfit() {
deferred = false;
return this;
}

4.6 resizeDimen() & resize()

调整图片大小。

1
2
3
4
5
6
7
8
9
10
11
12
13
/** Resize the image to the specified dimension size. */
public RequestCreator resizeDimen(int targetWidthResId, int targetHeightResId) {
Resources resources = picasso.context.getResources();
int targetWidth = resources.getDimensionPixelSize(targetWidthResId);
int targetHeight = resources.getDimensionPixelSize(targetHeightResId);
return resize(targetWidth, targetHeight);
}

/** Resize the image to the specified size in pixels. */
public RequestCreator resize(int targetWidth, int targetHeight) {
data.resize(targetWidth, targetHeight);
return this;
}

4.7 centerCrop() & centerInside()

centerCrop:充满ImageView居中裁剪
centerInside: 完整显示图片但是可能无法充满ImageView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Crops an image inside of the bounds specified by {@link #resize(int, int)} rather than
* distorting the aspect ratio. This cropping technique scales the image so that it fills the
* requested bounds and then crops the extra.
*/
public RequestCreator centerCrop() {
data.centerCrop();
return this;
}

/**
* Centers an image inside of the bounds specified by {@link #resize(int, int)}. This scales
* the image so that both dimensions are equal to or less than the requested bounds.
*/
public RequestCreator centerInside() {
data.centerInside();
return this;
}

4.8 onlyScaleDown()

当调用了resize 方法重新设置图片尺寸的时候,调用onlyScaleDown 方法,只有当原始图片的尺寸大于我们指定的尺寸时,resize才起作用。

1
2
3
4
5
6
7
8
/**
* Only resize an image if the original image size is bigger than the target size
* specified by {@link #resize(int, int)}.
*/
public RequestCreator onlyScaleDown() {
data.onlyScaleDown();
return this;
}

4.9 rotate()

旋转图片角度,可指定旋转中心点

1
2
3
4
5
6
7
8
9
10
11
/** Rotate the image by the specified degrees. */
public RequestCreator rotate(float degrees) {
data.rotate(degrees);
return this;
}

/** Rotate the image by the specified degrees around a pivot point. */
public RequestCreator rotate(float degrees, float pivotX, float pivotY) {
data.rotate(degrees, pivotX, pivotY);
return this;
}

4.10 config()

设置图片Bitmap格式,如
ALPHA_8,ARGB_4444,ARGB_8888,HARDWARE,RGBA_F16,RGB_565等等。

1
2
3
4
5
6
7
8
9
10
/**
* Attempt to decode the image using the specified config.
* <p>
* Note: This value may be ignored by {@link BitmapFactory}. See
* {@link BitmapFactory.Options#inPreferredConfig its documentation} for more details.
*/
public RequestCreator config(Bitmap.Config config) {
data.config(config);
return this;
}

4.11 stableKey()

设置固定Key

1
2
3
4
5
6
7
8
/**
* Sets the 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 RequestCreator stableKey(String stableKey) {
data.stableKey(stableKey);
return this;
}

4.12 priority()

设置优先级,这个对图片请求的执行顺序有影响,默认的优先级均为NORMAL

1
2
3
4
5
6
7
8
9
10
11
/**
* Set the priority of this request.
* <p>
* This will affect the order in which the requests execute but does not guarantee it.
* By default, all requests have {@link Priority#NORMAL} priority, except for
* {@link #fetch()} requests, which have {@link Priority#LOW} priority by default.
*/
public RequestCreator priority(Priority priority) {
data.priority(priority);
return this;
}

4.13 transform()

添加自定义Transformation,方便执行图形转换。
更多transformation请移步picasso-transformations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Add a custom transformation to be applied to the image.
* <p>
* Custom transformations will always be run after the built-in transformations.
*/
// TODO show example of calling resize after a transform in the javadoc
public RequestCreator transform(Transformation transformation) {
data.transform(transformation);
return this;
}
/**
* Add a list of custom transformations to be applied to the image.
* <p>
* Custom transformations will always be run after the built-in transformations.
*/
public RequestCreator transform(List<? extends Transformation> transformations) {
data.transform(transformations);
return this;
}

4.14 memoryPolicy()

指定memoryPolicy。NO_CACHE,NO_STORE
NO_CACHE:表示处理请求的时候跳过检查内存缓存
NO_STORE: 表示请求成功之后,结果不存到内存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Specifies the {@link MemoryPolicy} to use for this request. You may specify additional policy
* options using the varargs parameter.
*/
public RequestCreator memoryPolicy(MemoryPolicy policy, MemoryPolicy... additional) {
if (policy == null) {
throw new IllegalArgumentException("Memory policy cannot be null.");
}
this.memoryPolicy |= policy.index;
if (additional == null) {
throw new IllegalArgumentException("Memory policy cannot be null.");
}
if (additional.length > 0) {
for (MemoryPolicy memoryPolicy : additional) {
if (memoryPolicy == null) {
throw new IllegalArgumentException("Memory policy cannot be null.");
}
this.memoryPolicy |= memoryPolicy.index;
}
}
return this;
}

4.15 networkPolicy()

指定NetworkPolicy。NO_CACHE,NO_STORE,OFFLINE
NO_CACHE: 表示处理请求的时候跳过处理磁盘缓存
NO_STORE: 表示请求成功后,结果不缓存到Disk。
OFFLINE: 强制这次请求从缓存中获取结果,不会发起网络请求,不管缓存中能否获取到结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* Specifies the {@link NetworkPolicy} to use for this request. You may specify additional policy
* options using the varargs parameter.
*/
public RequestCreator networkPolicy(NetworkPolicy policy, NetworkPolicy... additional) {
if (policy == null) {
throw new IllegalArgumentException("Network policy cannot be null.");
}
this.networkPolicy |= policy.index;
if (additional == null) {
throw new IllegalArgumentException("Network policy cannot be null.");
}
if (additional.length > 0) {
for (NetworkPolicy networkPolicy : additional) {
if (networkPolicy == null) {
throw new IllegalArgumentException("Network policy cannot be null.");
}
this.networkPolicy |= networkPolicy.index;
}
}
return this;
}

4.16 noFade()

去掉默认的图片加载过程中的渐入效果

1
2
3
4
5
/** Disable brief fade in of images loaded from the disk cache or network. */
public RequestCreator noFade() {
noFade = true;
return this;
}

5. 其他用法

这里写图片描述

5.1 get()

同步方法,获取Bitmap。所以不要在主线程执行此操作
用法:

1
2
3
4
5
try {
Bitmap bitmap = Picasso.with(this).load(URL).get();
} catch (IOException e) {
e.printStackTrace();
}

源码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* Synchronously fulfill this request. Must not be called from the main thread.
* <p>
* <em>Note</em>: The result of this operation is not cached in memory because the underlying
* {@link Cache} implementation is not guaranteed to be thread-safe.
*/
public Bitmap get() throws IOException {
long started = System.nanoTime();
checkNotMain();

if (deferred) {
throw new IllegalStateException("Fit cannot be used with get.");
}
if (!data.hasImage()) {
return null;
}

Request finalData = createRequest(started);
String key = createKey(finalData, new StringBuilder());

Action action = new GetAction(picasso, finalData, memoryPolicy, networkPolicy, tag, key);
return forRequest(picasso, picasso.dispatcher, picasso.cache, picasso.stats, action).hunt();
}

5.2 fetch()

毫无结果的获取图片,也没有目标,也没有返回bitmap,难道只是为了暖场?考虑到Picasso有缓存机制,这个方法还是有一点价值的,提前加载放到缓存,后面加载速度会更快。

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
/**
* Asynchronously fulfills the request without a {@link ImageView} or {@link Target},
* and invokes the target {@link Callback} with the result. This is useful when you want to warm
* up the cache with an image.
* <p>
* <em>Note:</em> The {@link Callback} param is a strong reference and will prevent your
* {@link android.app.Activity} or {@link android.app.Fragment} from being garbage collected
* until the request is completed.
*/
public void fetch(Callback callback) {
long started = System.nanoTime();

if (deferred) {
throw new IllegalStateException("Fit cannot be used with fetch.");
}
if (data.hasImage()) {
// Fetch requests have lower priority by default.
if (!data.hasPriority()) {
data.priority(Priority.LOW);
}

Request request = createRequest(started);
String key = createKey(request, new StringBuilder());
Bitmap bitmap = picasso.quickMemoryCacheCheck(key);

if (bitmap != null) {
if (picasso.loggingEnabled) {
log(OWNER_MAIN, VERB_COMPLETED, request.plainId(), "from " + MEMORY);
}
if (callback != null) {
callback.onSuccess();
}
} else {
Action action =
new FetchAction(picasso, request, memoryPolicy, networkPolicy, tag, key, callback);
picasso.submit(action);
}
}
}

6. 自定义Picasso

默认使用Picasso.with(context)可以快速的构建Picasso实例,但是Picasso支持扩展自定义Picasso,也就是一些默认的配置,我们可以自己来调控。
这里写图片描述

可以看到Picasso.Builder方法存在很多可配置的内容,downloader, executor, memoryCache,listener等等都是可以自己设置的,配置完成后调用
builder()方法即可返回Picasso实例。
还可以通过如下方法设置全局Picasso实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Set the global instance returned from {@link #with}.
* <p>
* This method must be called before any calls to {@link #with} and may only be called once.
*/
public static void setSingletonInstance(Picasso picasso) {
synchronized (Picasso.class) {
if (singleton != null) {
throw new IllegalStateException("Singleton instance already exists.");
}
singleton = picasso;
}
}

7. 类图

请适当放大后查看。
这里写图片描述

8. 主要流程图

请适当放大后查看。
这里写图片描述

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×