Square公司开源的图片加载库。优点是功能还算完善,能满足基本的图片加载需求,使用简单,体量小。
官方链接:
http://square.github.io/picasso/
Git: https://github.com/square/picasso
本篇文章基于Picasso 2.71828,从基本的使用一步步分析
Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);
从get()方法来看,这个版本的Picasso和之前有所修改,之前用过的同学应该知道,之前是采用的with()方法
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 Check的同步锁,singleton是volatile类型的,构造了一个Picasso对象,接着看PicassoProvider,这个类继承于ContentProvider,在onCreate方法中用getContext()获取了Context。然后看build方法
** Create the {@link Picasso} instance. */
public Picasso build() {
Context context = this.context;
if (downloader == null) {
downloader = new OkHttp3Downloader(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);
}
}
方法的注释写了,这个方法是创建Picasso的instance,这里面构造了
downloader :负责图片的实际加载
cache:内存缓存,LruCache,看过它源码的同学应该知道,他由LinkedHashMap实现,里面实际上是一个双向链表,每次修改会把最新的对象放到链表尾端,当链表达到最大长度,就会删除链表头的元素,这样就可以保留最近被使用的对象
service:Picasso的线程池
PicassoExecutorService() {
super(DEFAULT_THREAD_COUNT, DEFAULT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(), new Utils.PicassoThreadFactory());
}
DEFAULT_THREAD_COUNT=3,核心线程数和最大线程都是3个,提交的阻塞队列为具有优先级的无界队列队列,blockingQueue的实现原理就是Lock配合Full和Empty两个信号量,对队列进行操作的。
transformer:请求转换器
Stats:表示缓存的状态,Bitmap和download的状态
dispatcher:请求分发器,很重要,后面的文章分析。
load方法
public RequestCreator load(@Nullable 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方法直接返回了一个RequestCreator,这个类是用来加载请求的,这里会创建一个Request builder对象,传入了图片的Uri,resourceId,以及BitmapConfig。返回了一个RequestCreator,方便链式调用。这个类还可以设置placeHolder,error等参数。
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);
}
into方法
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);
}
首先会检测是否在主线程运行,通过getMainLooper的Thread去判断的。
static boolean isMain() {
return Looper.getMainLooper().getThread() == Thread.currentThread();
}
接着判断图片信息和target是否为null,否则取消请求。
接着判断是否需要取缓存,取到了缓存就取消请求
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;
}
}
这里两次提到了取消请求,我们看一下他是怎么做的
void cancelExistingRequest(Object target) {
checkMain();
Action action = targetToAction.remove(target);
if (action != null) {
action.cancel();
dispatcher.dispatchCancel(action);
}
if (target instanceof ImageView) {
ImageView targetImageView = (ImageView) target;
DeferredRequestCreator deferredRequestCreator =
targetToDeferredRequestCreator.remove(targetImageView);
if (deferredRequestCreator != null) {
deferredRequestCreator.cancel();
}
}
}
这里在检测主线程后,出现了一个Action类,Action可以看做单个图片加载的任务model,抽象类。是一个Request的包装类。
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<>(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);
}
targetToAction是一个Map,key为imageView,value为Action,取消请求时首先会移除这个值。然后在cancle dispatcher,这里面也是通过handler message来实现的,看到具体的实现
void performCancel(Action action) {
String key = action.getKey();
BitmapHunter hunter = hunterMap.get(key);
if (hunter != null) {
hunter.detach(action);
if (hunter.cancel()) {
hunterMap.remove(key);
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_CANCELED, action.getRequest().logId());
}
}
}
if (pausedTags.contains(action.getTag())) {
pausedActions.remove(action.getTarget());
if (action.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_CANCELED, action.getRequest().logId(),
"because paused request got canceled");
}
}
Action remove = failedActions.remove(action.getTarget());
if (remove != null && remove.getPicasso().loggingEnabled) {
log(OWNER_DISPATCHER, VERB_CANCELED, remove.getRequest().logId(), "from replaying");
}
}
这里又出现了一个新类,BitmaoHunter,这是一个Runnable,用来开启线程下载,同时还可以解码图片,重点关注hunt方法,这里面会调用RequestHandler去下载图片,RequestHandler是一个抽象类,我们可以看NetworkRequestHandler中的load方法,这里会调用OkHttp去下载图片。
接着回去看CancelRequest的下半部分
if (target instanceof ImageView) {
ImageView targetImageView = (ImageView) target;
DeferredRequestCreator deferredRequestCreator =
targetToDeferredRequestCreator.remove(targetImageView);
if (deferredRequestCreator != null) {
deferredRequestCreator.cancel();
}
}
这里有一个DeferredRequestCreator类,这个类会对我们的target(ImageView)进行监听,它用来辅助获取我们ImageView的宽和高,然后就会重新执行请求创建。这里我们同样需要remove掉这里面的记录。
这篇就暂时先到这里,后面我们继续分析Dispatcher和into后面的部分。