Picasso.get().load(url).into(iv);
Picasso的常见使用步骤很简单,下面我们概要的看看其实现。
1. 实例获取get()
/* Picasso.class */
@SuppressLint("StaticFieldLeak") static volatile Picasso singleton = null;
public static Picasso get() {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
if (PicassoProvider.context == null) {
throw new IllegalStateException("context == null");
}
singleton = new Builder(PicassoProvider.context).build();
}
}
}
return singleton;
}
使用double checked locking单例模式创建Picasso实例,创建过程中使用建造者Builder。
1.1 context获取
通过ContentProvider,在AndroidManifest 中注册PicassoProvider,app启动后调用onCreate()
即可获取所需context实例。
public final class PicassoProvider extends ContentProvider {
@SuppressLint("StaticFieldLeak") static Context context;
@Override public boolean onCreate() {
context = getContext();
return true;
}
}
1.2 Picasso创建
使用Builder Pattern建造者模式,将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示,简单点说就是用多个简单的对象一步一步构建成一个复杂的对象。
/* Picasso.class */
public Builder(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null.");
}
this.context = context.getApplicationContext();
}
具体建造过程如下。
/* Picasso.class */
public Picasso build() {
Context context = this.context;
// 配置下载器,下载网络图片资源(默认使用OkHttp3Downloader)
if (downloader == null) {
downloader = new OkHttp3Downloader(context);
}
// 配置内存缓存,提高图片加载效率(默认使用LruCache[Least Recently Used])
// 内存缓存大小为RAM的15%;磁盘缓存大小为ROM的2%且范围为5~50mb
if (cache == null) {
cache = new LruCache(context);
}
// 配置线程池
// 默认3个线程;使用wifi为4个,4G为3个,3G为2个,2G为1个
if (service == null) {
service = new PicassoExecutorService();
}
// 配置请求转换器(默认不做处理,直接返回原请求)
if (transformer == null) {
transformer = RequestTransformer.IDENTITY;
}
// 统计:统计缓存命中数量、图片下载数量等
Stats stats = new Stats(cache);
// 分发器:由HANDLER分发图片请求、请求完成等等事件
Dispatcher dispatcher = new Dispatcher(context, service, HANDLER, downloader, cache, stats);
return new Picasso(context, dispatcher, cache, listener, transformer, requestHandlers, stats,
defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
至此,Picasso.get()
过程完成。
2. 获取请求load(url)
即通过请求创建器RequestCreator创建请求Request,通过Request可以执行placeholder(placeholderResId)方法设置占位图,error(errorResId)方法设置错误图,noFade()方法取消图片渐变效果等操作。
/* Picasso.class */
public RequestCreator load(@Nullable String path) {
...
return load(Uri.parse(path));
}
public RequestCreator load(@Nullable Uri uri) {
return new RequestCreator(this, uri, 0);
}
RequestCreator的构造方法如下,核心是通过建造者模式建造Request实例。
通过picasso可以发起请求、推迟(defer)请求、取消请求、检查缓存配置等操作;通过创建的Request实例data可以调用centerCrop()、centerInside()等方法。
boolean shutdown;
private final Picasso picasso;
private final Request.Builder data;
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. 显示into(iv)
核心逻辑为创建Action,即一次图片请求的活动过程,然后分发该action
/* RequestCreator.class */
/**
* 异步请求图片资源至用户指定的ImageView
* 使用弱引用关联ImageView,资源将会被自动回收
*/
public void into(ImageView target) {
into(target, null);
}
/**
* 若用户指定Callback,为了防止Activity/Fragment回收,会是一个强引用;推荐及时调用cancelRequest()方法防止内存泄露
*/
public void into(ImageView target, Callback callback) {
// 记录开始时间,并检查线程是否安全
long started = System.nanoTime();
checkMain();
if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}
// 若Request实例为空,取消当前请求,设置占位图
if (!data.hasImage()) {
picasso.cancelRequest(target);
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
return;
}
// deferred默认是false的
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());
}
// 若获取不到目标ImageView的宽高,就延迟请求,直到获取宽高时再次调用into()方法
picasso.defer(target, new DeferredRequestCreator(this, target, callback));
return;
}
data.resize(width, height);
}
// 创建Request实例并标记
Request request = createRequest(started);
String requestKey = createKey(request);
// 控制从缓存中读取,若命中requestKey对应的Bitmap则取消当前请求,并显示图片
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实例,并提交至picasso
Action action =
new ImageViewAction(picasso, target, request, memoryPolicy, networkPolicy, errorResId,
errorDrawable, requestKey, tag, callback, noFade);
picasso.enqueueAndSubmit(action);
}
3.1 action的创建
上述代码中用到的关键活动类ImageViewAction就是继承自Action,并重写了Action中的方法complete()、error()、cancel()
,具体实现和方法注释说明如下。
class ImageViewAction extends Action<ImageView> {
Callback callback;
ImageViewAction(Picasso picasso, ImageView imageView, Request data, int memoryPolicy,
int networkPolicy, int errorResId, Drawable errorDrawable, String key, Object tag,
Callback callback, boolean noFade) {
super(picasso, imageView, data, memoryPolicy, networkPolicy, errorResId, errorDrawable, key,
tag, noFade);
this.callback = callback;
}
// 图片请求成功,使用PicassoDrawable将图片显示到指定的ImageView控件中
@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();
}
}
// 图片请求失败,判断是否显示占位图
@Override public void error(Exception e) {
ImageView target = this.target.get();
if (target == null) {
return;
}
Drawable placeholder = target.getDrawable();
if (placeholder instanceof Animatable) {
((Animatable) placeholder).stop();
}
if (errorResId != 0) {
target.setImageResource(errorResId);
} else if (errorDrawable != null) {
target.setImageDrawable(errorDrawable);
}
if (callback != null) {
callback.onError(e);
}
}
// 取消本次请求
@Override void cancel() {
super.cancel();
if (callback != null) {
callback = null;
}
}
}
3.2 action的提交
picasso提交action,然后通过分发器dispatcher分发消息。
/* Picasso.class */
void enqueueAndSubmit(Action action) {
...
submit(action);
}
void submit(Action action) {
dispatcher.dispatchSubmit(action);
}
3.3 action的分发
dispatcher通过Handler发送action,交由图片捕获器BitmapHunter处理。
/* Dispatcher.class */
void dispatchSubmit(Action action) {
handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
}
@Override public void handleMessage(final Message msg) {
switch (msg.what) {
case REQUEST_SUBMIT: {
Action action = (Action) msg.obj;
dispatcher.performSubmit(action);
break;
...
}
}
}
void performSubmit(Action action) {
performSubmit(action, true);
}
void performSubmit(Action action, boolean dismissFailed) {
...
// 剩下的工作将由BitmapHunter完成
BitmapHunter hunter = BitmapHunter.forRequest(action.getPicasso(), this, cache, stats, action);
hunter.future = service.submit(hunter);
...
}
3.3.1 BitmapHunter.forRequest()
获取图片捕获器
/* BitmapHunter.class */
static BitmapHunter forRequest(Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action) {
Request request = action.getRequest();
List<RequestHandler> requestHandlers = picasso.getRequestHandlers();
for (int i = 0, count = requestHandlers.size(); i < count; i++) {
RequestHandler requestHandler = requestHandlers.get(i);
// 轮询获取能够处理这次活动请求(action's request)的请求处理器
if (requestHandler.canHandleRequest(request)) {
return new BitmapHunter(picasso, dispatcher, cache, stats, action, requestHandler);
}
}
return new BitmapHunter(picasso, dispatcher, cache, stats, action, ERRORING_HANDLER);
}
其中所有的请求处理器requestHandlers是在Picasso构造时初始化的,总共为7种。
Picasso(...) {
...
allRequestHandlers.add(new ResourceRequestHandler(context));
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));
...
}
看看网络请求处理器中的匹配规则,即Uri中包含"http"或"https"。
/* NetworkRequestHandler.class */
@Override public boolean canHandleRequest(Request data) {
String scheme = data.uri.getScheme();
return ("http".equals(scheme) || "https".equals(scheme));
}
3.3.2 service.submit(hunter)
开启图片捕获器hunter线程,获取bitmap
图片捕获器hunter是一个Runnable,hunter获取后执行submit()
方法开启hunter线程。
/* ExecutorService.class */
public Future<?> submit(Runnable task) {
...
execute(ftask);
return ftask;
}
hunter线程中的核心操作就是获取图片资源,然后分发获取结果
/* BitmapHunter.class */
@Override public void run() {
Bitmap result = hunt();
if (result == null) {
dispatcher.dispatchFailed(this);
} else {
dispatcher.dispatchComplete(this);
}
...
}
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
// 从缓存中读取图片资源
if (shouldReadFromMemoryCache(memoryPolicy)) {
bitmap = cache.get(key);
...
return bitmap;
}
// 缓存未命中,使用请求处理器加载图片资源
RequestHandler.Result result = requestHandler.load(data, networkPolicy);
if (result != null) {
...
bitmap = result.getBitmap();
...
}
...
return bitmap;
}
本篇以请求网络图片为背景展开介绍,load()
方法中使用下载器downloader来下载图片,当前Picasso版本中的网络下载器貌似只有OkHttp3Downloader,没有UrlConnectionDownloader了。
网络请求处理器NetworkRequestHandler中的load()
方法如下,即使用下载器下载图片资源。
/* NetworkRequestHandler.class */
@Override public Result load(Request request, int networkPolicy) throws IOException {
okhttp3.Request downloaderRequest = createRequest(request, networkPolicy);
Response response = downloader.load(downloaderRequest);
ResponseBody body = response.body();
...
return new Result(body.source(), loadedFrom);
}
网络下载器OkHttp3Downloader中的load()
方法如下,请求完成即可获取图片资源。
/* OkHttp3Downloader.class */
@NonNull @Override public Response load(@NonNull Request request) throws IOException {
return client.newCall(request).execute();
}
至此图片资源获取bitmap完成,hunter执行dispatcher.dispatchComplete(this);
方法来将图片获取结果分发出去,最终图片会显示至ImageView控件,下面看看这个分发过程。
3.3.3 bitmap获取后,消息分发
/* Dispatcher.class */
void dispatchComplete(BitmapHunter hunter) {
handler.sendMessage(handler.obtainMessage(HUNTER_COMPLETE, hunter));
}
/* Dispatcher.class */
case HUNTER_COMPLETE: {
BitmapHunter hunter = (BitmapHunter) msg.obj;
dispatcher.performComplete(hunter);
break;
}
/* Dispatcher.class */
void performComplete(BitmapHunter hunter) {
...
batch(hunter);
...
}
/* Dispatcher.class */
private void batch(BitmapHunter hunter) {
...
batch.add(hunter);
if (!handler.hasMessages(HUNTER_DELAY_NEXT_BATCH)) {
handler.sendEmptyMessageDelayed(HUNTER_DELAY_NEXT_BATCH, BATCH_DELAY);
}
}
/* Dispatcher.class */
case HUNTER_DELAY_NEXT_BATCH: {
dispatcher.performBatchComplete();
break;
}
/* Dispatcher.class */
void performBatchComplete() {
List<BitmapHunter> copy = new ArrayList<>(batch);
batch.clear();
mainThreadHandler.sendMessage(mainThreadHandler.obtainMessage(HUNTER_BATCH_COMPLETE, copy));
logBatch(copy);
}
/* Picasso.class */
case HUNTER_BATCH_COMPLETE: {
@SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
for (int i = 0, n = batch.size(); i < n; i++) {
BitmapHunter hunter = batch.get(i);
hunter.picasso.complete(hunter);
}
break;
}
/* Picasso.class */
void complete(BitmapHunter hunter) {
Action single = hunter.getAction();
...
Exception exception = hunter.getException();
Bitmap result = hunter.getResult();
LoadedFrom from = hunter.getLoadedFrom();
if (single != null) {
deliverAction(result, from, single, exception);
}
...
}
/* Picasso.class */
private void deliverAction(Bitmap result, LoadedFrom from, Action action, Exception e) {
...
action.complete(result, from);
...
}
最后,本次action资源请求成功,调用其complete()
方法,通过PicassoDrawable的setBitmap()
方法来将图片显示至目标控件中。
/* ImageViewAction.class */
@Override public void complete(Bitmap result, Picasso.LoadedFrom from) {
...
PicassoDrawable.setBitmap(target, context, result, from, noFade, indicatorsEnabled);
...
}
PicassoDrawable的setBitmap()
方法中,将获取到的bitmap转为drawable,然后设置进ImageView控件中,即可完成图片的显示。
/* PicassoDrawable.class */
static void setBitmap(ImageView target, Context context, Bitmap bitmap, Picasso.LoadedFrom loadedFrom, boolean noFade, boolean debugging) {
...
PicassoDrawable drawable = new PicassoDrawable(context, bitmap, placeholder, loadedFrom, noFade, debugging);
target.setImageDrawable(drawable);
}
小结:上述流程即先在
get()
方法中创建Picasso,其中包含下载器downloader、缓存器cache、线程池service、请求转换器transformer、分发器dispatcher。然后在load(url)
方法中创建请求Request实例,通过请求实例可以设置占位图,调整图片显示尺寸等操作。紧接着在into(iv)
方法中创建一个请求图片的活动action,action被提交给了图片捕获器hunter线程,hunter启动后,先为action匹配合适的请求处理器requestHandler,然后requestHandler使用下载器downloader从网络下载图片资源,网络请求完成后,通过分发器dispatcher告知action本次图片资源获取成功。最后在action的complete()
方法中使用PicassoDrawable将图片设置进目标控件,完成图片的显示。
4. 额外说明
下面对Picasso对象的各个核心内部对象简要的看一下。
4.1 下载器downloader
当前Picasso版本的下载器只有OkHttp3Downloader。
4.2. 缓存器cache
使用的是LruCache,具体的内存和磁盘缓存空间为RAM的15%和磁盘的2%且范围为5~50mb。
memory cache of 15% the available application RAM,Disk cache of 2% storage space up to 50MB but no less than 5MB。
4.3. 线程池service
默认池数为3,网络状态为wifi时为4,4G时为3,3G时为2,2G时为1。
private static final int DEFAULT_THREAD_COUNT = 3;
switch (info.getType()) {
case ConnectivityManager.TYPE_WIFI:
case ConnectivityManager.TYPE_WIMAX:
case ConnectivityManager.TYPE_ETHERNET:
setThreadCount(4);
break;
case ConnectivityManager.TYPE_MOBILE:
switch (info.getSubtype()) {
case TelephonyManager.NETWORK_TYPE_LTE: // 4G
setThreadCount(3);
break;
case TelephonyManager.NETWORK_TYPE_UMTS: // 3G
setThreadCount(2);
break;
case TelephonyManager.NETWORK_TYPE_GPRS: // 2G
setThreadCount(1);
break;
}
break;
}
4.4. 请求转换器transformer
默认为不做任何转换处理,直接返回原请求。
RequestTransformer IDENTITY = new RequestTransformer() {
@Override public Request transformRequest(Request request) {
return request;
}
};
4.5. 分发器dispatcher
主要就是通过Handler来分发各种消息。
网友评论