Picasso 是 Square 开源的图片缓存库,主要特点有:
- 包含内存缓存和磁盘缓存两级缓存。
- 在 Adapter 中自动处理 ImageView 的缓存并且取消之前的图片下载任务。
- 方便进行图片转换处理。
基本使用
Picasso.with(this) //Context
.load("http://7xjung.com1.z0.glb.clouddn.com/randomview.png") //图片地址
.placeholder(R.drawable.abc_btn_check_material) //占位图(等待下载的图片)
.error(R.drawable.abc_btn_radio_material) //加载错误显示的图片
.resize(200,200) //设置显示大小
.centerCrop() //适应布局,减小内存
.into(mLoadIv); //用来显示图片控件的ID
下载地址 : https://github.com/square/picasso
分析工作流程
分析第一次加载图片的情况,没有缓存。
Picasso.with(this):
public static Picasso with(Context context) {
if (singleton == null) {
synchronized (Picasso.class) {
if (singleton == null) {
singleton = new Builder(context).build(); //获取实例对象
}
}
}
return singleton;
}
//这里使用了单例模式,而且是双重检测锁的方法。 另外还使用到了Builder模式,Picasso对象的创建非常复杂。
得到的是context.getApplicationContext();
public Builder(Context context) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null.");
}
this.context = context.getApplicationContext();
}
Picasso的部分成员变量
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;
看下build()方法,返回Picasso,初始化各种服务,下载器,缓存器,线程池,是否resize图片,调度器
public Picasso build() {
Context context = this.context;
if (downloader == null) {
downloader = Utils.createDefaultDownloader(context);
}
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,
defaultBitmapConfig, indicatorsEnabled, loggingEnabled);
}
}
//这里先初始化Downloader,Cache(使用的LRU算法),PicassoExecutorService(这是Picasso自己实现的线程池),Transformer(当请求被submit时可以调用来修改请求)。
之后调用load(),有4个重载方法
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)); //调用load(Uri uri)
}
public RequestCreator load(File file) {
if (file == null) {
return new RequestCreator(this, null, 0);
}
return load(Uri.fromFile(file)); //调用load(Uri uri)
}
public RequestCreator load(int resourceId) {
if (resourceId == 0) {
throw new IllegalArgumentException("Resource ID must not be zero.");
}
return new RequestCreator(this, null, resourceId);
}
public RequestCreator load(Uri uri) {
return new RequestCreator(this, uri, 0);
}
最终都是执行
new RequestCreator(this, uri, 0)
那么RequsetCreator是什么呢?(创造图片下载的请求)
private final Request.Builder data; //this.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);
}
Request类中的Builder()方法,初始化
Builder(Uri uri, int resourceId, Bitmap.Config bitmapConfig) {
this.uri = uri;
this.resourceId = resourceId;
this.config = bitmapConfig;
}
之后是into()
public void into(ImageView target) {
into(target, null);
}
关键在于下面这个方法:
public void into(ImageView target, Callback callback) {
long started = System.nanoTime();
checkMain(); //Looper.getMainLooper().getThread() == Thread.currentThread(),检查是不是主线程执行的。
if (target == null) {
throw new IllegalArgumentException("Target must not be null.");
}
if (!data.hasImage()) { //boolean hasImage() {return uri != null || resourceId != 0;},是否为空
picasso.cancelRequest(target);
if (setPlaceholder) {
setPlaceholder(target, getPlaceholderDrawable());
}
return;
}
if (deferred) { //public RequestCreator fit() {deferred = true;return this;}判断是否改变图片大小,适配控件;deferred在这里是默认值false
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); //这里创建了请求,给请求唯一的id,url,并且转换request
String requestKey = createKey(request); //给请求创建了一个key
if (shouldReadFromMemoryCache(memoryPolicy)) { //读取缓存
Bitmap bitmap = picasso.quickMemoryCacheCheck(requestKey);
if (bitmap != null) {
picasso.cancelRequest(target);//如果cache里能找到,就取消请求,直接设置
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, memoryPolicy, networkPolicy, errorResId,
errorDrawable, requestKey, tag, callback, noFade);
picasso.enqueueAndSubmit(action);
}
创建回调方法:下载完图片,再通过控件显示
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;
}
Action构造方法
Action(Picasso picasso, T target, Request request, int memoryPolicy, int networkPolicy,
int errorResId, Drawable errorDrawable, String key, Object tag, boolean noFade) {
this.picasso = picasso;
this.request = request;
this.target =
target == null ? null : new RequestWeakReference<T>(this, target, picasso.referenceQueue);
this.memoryPolicy = memoryPolicy;
this.networkPolicy = networkPolicy;
this.noFade = noFade;
this.errorResId = errorResId;
this.errorDrawable = errorDrawable;
this.key = key;
this.tag = (tag != null ? tag : this);
}
// Action代表了一个具体的加载任务
Action中用了一个弱引用,创建请求队列
static class RequestWeakReference<M> extends WeakReference<M> {
final Action action;
public RequestWeakReference(Action action, M referent, ReferenceQueue<? super M> q) {
super(referent, q);
this.action = action;
}
}
最后调用的一个方法: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); // final Map<Object, Action> targetToAction;映射结构
}
submit(action);
}
分给调度器执行任务(分发任务)
final Dispatcher dispatcher;
void submit(Action action) {
dispatcher.dispatchSubmit(action);
}
发送到消息队列
void dispatchSubmit(Action action) {
handler.sendMessage(handler.obtainMessage(REQUEST_SUBMIT, action));
}
看下handler如何工作的?
//Dispatcher构造方法的语句
this.handler = new DispatcherHandler(dispatcherThread.getLooper(), this);
定义了一个静态内部类,执行不同的请求:
private static class DispatcherHandler extends Handler {
private final Dispatcher dispatcher;
public DispatcherHandler(Looper looper, Dispatcher dispatcher) {
super(looper);
this.dispatcher = dispatcher;
}
@Override public void handleMessage(final Message msg) {
switch (msg.what) {
case REQUEST_SUBMIT: {
Action action = (Action) msg.obj;
dispatcher.performSubmit(action);
break;
}
......//省略其它case
}
}
}
请求REQUEST_SUBMIT之后的方法:
void performSubmit(Action action) {
performSubmit(action, true);
}
void performSubmit(Action action, boolean dismissFailed) {
if (pausedTags.contains(action.getTag())) {
pausedActions.put(action.getTarget(), action); // final Map<Object, Action> pausedActions;
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()); //BitmapHunter实现了Runnable方法
if (hunter != null) {
hunter.attach(action); //注意这个attach(),将action加入到一个List中,actions.add(action);
return;
}
if (service.isShutdown()) {
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_IGNORED, action.request.logId(), "because shut down");
}
return;
}
hunter = forRequest(action.getPicasso(), this, cache, stats, action);
hunter.future = service.submit(hunter); //交给线程池去执行, 初始化的PicassoExecutorService, <T> Future<T> submit(Runnable task, T result);
hunterMap.put(action.getKey(), hunter); //管理起来,防止任务重复
if (dismissFailed) {
failedActions.remove(action.getTarget());
}
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_ENQUEUED, action.request.logId());
}
}
//线程池中结合优先级去执行任务的,之前有设定Picasso类中的枚举类型Priority。
终于找到了图片加载的来源,就是BitmapHunter的run()执行的:
result = hunt()
下面是hunt()方法
Bitmap hunt() throws IOException {
Bitmap bitmap = null;
if (shouldReadFromMemoryCache(memoryPolicy)) {
bitmap = cache.get(key);
if (bitmap != null) {
stats.dispatchCacheHit();
loadedFrom = MEMORY;
if (picasso.loggingEnabled) {
log(OWNER_HUNTER, VERB_DECODED, data.logId(), "from cache");
}
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;
}
Picasso的Handler来处理接受到的消息(主线程中)
static final Handler HANDLER = new Handler(Looper.getMainLooper()) {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case HUNTER_BATCH_COMPLETE: {
@SuppressWarnings("unchecked") List<BitmapHunter> batch = (List<BitmapHunter>) msg.obj;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, n = batch.size(); i < n; i++) {
BitmapHunter hunter = batch.get(i);
hunter.picasso.complete(hunter); //这里获取到result
}
break;
}
case REQUEST_GCED: {
Action action = (Action) msg.obj;
if (action.getPicasso().loggingEnabled) {
log(OWNER_MAIN, VERB_CANCELED, action.request.logId(), "target got garbage collected");
}
action.picasso.cancelExistingRequest(action.getTarget());
break;
}
case REQUEST_BATCH_RESUME:
@SuppressWarnings("unchecked") List<Action> batch = (List<Action>) msg.obj;
//noinspection ForLoopReplaceableByForEach
for (int i = 0, n = batch.size(); i < n; i++) {
Action action = batch.get(i);
action.picasso.resumeAction(action);
}
break;
default:
throw new AssertionError("Unknown handler message received: " + msg.what);
}
}
};
完成后是调用的ImageViewAction里的方法,
@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(); //调用回调方法
}
}
//关于线程池的分析又是一大波。。。暂时不贴上
主要涉及的类:Picasso,Request,RequestCreator, Action, ImageViewAction, Utils,Dispatcher, BitmapHunter…