picasso使用简单,如下
picasso.with(context).load("http://i.imgur.com/dvpvklr.png").into(imageview);
主要有以下一些特性:
在adapter中回收和取消当前的下载;
使用最少的内存完成复杂的图形转换操作;
自动的内存和硬盘缓存;
图形转换操作,如变换大小,旋转等,提供了接口来让用户可以自定义转换操作;
加载载网络或本地资源;
lrucacha,主要是get和set方法,存储的结构采用了linkedhashmap,这种map内部实现了lru算法(least recently used 近期最少使用算法)。
this.map = new linkedhashmap<string, bitmap>(0, 0.75f, true);
最后一个参数的解释:
true if the ordering should be done based on the last access (from least-recently accessed to most-recently accessed), and false if the ordering should be the order in which the entries were inserted.
因为可能会涉及多线程,所以在存取的时候都会加锁。而且每次set操作后都会判断当前缓存区是否已满,如果满了就清掉最少使用的图形。代码如下
private void trimtosize(int maxsize) {
while (true) {
string key;
bitmap value;
synchronized (this) {
if (size < 0 || (map.isempty() && size != 0)) {
throw new illegalstateexception(getclass().getname()
+ ".sizeof() is reporting inconsistent results!");
}
if (size <= maxsize || map.isempty()) {
break;
map.entry<string, bitmap> toevict = map.entryset().iterator()
.next();
key = toevict.getkey();
value = toevict.getvalue();
map.remove(key);
size -= utils.getbitmapbytes(value);
evictioncount++;
}
}
}
所有对图形的操作都会记录在这里,供之后图形的创建使用,如重新计算大小,旋转角度,也可以自定义变换,只需要实现transformation,一个bitmap转换的接口。
public interface transformation {
/**
* transform the source bitmap into a new bitmap. if you create a new bitmap instance, you must
* call {@link android.graphics.bitmap#recycle()} on {@code source}. you may return the original
* if no transformation is required.
*/
bitmap transform(bitmap source);
* returns a unique key for the transformation, used for caching purposes. if the transformation
* has parameters (e.g. size, scale factor, etc) then these should be part of the key.
string key();
当操作封装好以后,会将request传到另一个结构中action。
action代表了一个具体的加载任务,主要用于图片加载后的结果回调,有两个抽象方法,complete和error,也就是当图片解析为bitmap后用户希望做什么。最简单的就是将bitmap设置给imageview,失败了就将错误通过回调通知到上层。
imageviewaction实现了action,在complete中将bitmap和imageview组成了一个picassodrawable,里面会实现淡出的动画效果。
@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 debugging = picasso.debugging;
picassodrawable.setbitmap(target, context, result, from, nofade,
debugging);
if (callback != null) {
callback.onsuccess();
}
有了加载任务,具体的图片下载与解析是在哪里呢?这些都是耗时的操作,应该放在异步线程中进行,就是下面的bitmaphunter。
bitmaphunter是一个runnable,其中有一个decode的抽象方法,用于子类实现不同类型资源的解析。
public void run() {
try {
thread.currentthread()
.setname(utils.thread_prefix + data.getname());
result = hunt();
if (result == null) {
dispatcher.dispatchfailed(this);
} else {
dispatcher.dispatchcomplete(this);
} catch (ioexception e) {
exception = e;
dispatcher.dispatchretry(this);
} catch (exception e) {
dispatcher.dispatchfailed(this);
} finally {
thread.currentthread().setname(utils.thread_idle_name);
abstract bitmap decode(request data) throws ioexception;
bitmap hunt() throws ioexception {
bitmap bitmap;
if (!skipmemorycache) {
bitmap = cache.get(key);
if (bitmap != null) {
stats.dispatchcachehit();
loadedfrom = memory;
return bitmap;
bitmap = decode(data);
if (bitmap != null) {
stats.dispatchbitmapdecoded(bitmap);
if (data.needstransformation() || exifrotation != 0) {
synchronized (decode_lock) {
if (data.needsmatrixtransform() || exifrotation != 0) {
bitmap = transformresult(data, bitmap, exifrotation);
}
if (data.hascustomtransformations()) {
bitmap = applycustomtransformations(
data.transformations, bitmap);
stats.dispatchbitmaptransformed(bitmap);
return bitmap;
可以看到,在decode生成原始bitmap,之后会做需要的转换transformresult和applycustomtransformations。最后在将最终的结果传递到上层dispatcher.dispatchcomplete(this)。
基本的组成元素有了,那这一切是怎么连接起来运行呢,答案是dispatcher。
在bitmaphunter成功得到bitmap后,就是通过dispatcher将结果传递出去的,当然让bitmaphunter执行也要通过dispatcher。
dispatcher内有一个handlerthread,所有的请求都会通过这个thread转换,也就是请求也是异步的,这样应该是为了ui线程更加流畅,同时保证请求的顺序,因为handler的消息队列。
外部调用的是dispatchxxx方法,然后通过handler将请求转换到对应的performxxx方法。
例如生成action以后就会调用dispather的dispatchsubmit()来请求执行,
void dispatchsubmit(action action) {
handler.sendmessage(handler.obtainmessage(request_submit, action));
handler接到消息后转换到performsubmit方法
void performsubmit(action action) {
bitmaphunter hunter = huntermap.get(action.getkey());
if (hunter != null) {
hunter.attach(action);
if (service.isshutdown()) {
hunter = forrequest(context, action.getpicasso(), this, cache, stats,
action, downloader);
hunter.future = service.submit(hunter);
huntermap.put(action.getkey(), hunter);
这里将通过action得到具体的bitmaphunder,然后交给executorservice执行。
下面是picasso.with(context).load("http://i.imgur.com/dvpvklr.png").into(imageview)的过程,
public static picasso with(context context) {
if (singleton == null) {
singleton = new builder(context).build();
return singleton;
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, stats, debugging);
在picasso.with()的时候会将执行所需的所有必备元素创建出来,如缓存cache、执行executorservice、调度dispatch等,在load()时创建request,在into()中创建action、bitmaphunter,并最终交给dispatcher执行。