天天看点

picasso图片缓存框架 代码分析

picasso使用简单,如下

picasso图片缓存框架 代码分析

picasso.with(context).load("http://i.imgur.com/dvpvklr.png").into(imageview);  

主要有以下一些特性:

在adapter中回收和取消当前的下载;

使用最少的内存完成复杂的图形转换操作;

自动的内存和硬盘缓存;

图形转换操作,如变换大小,旋转等,提供了接口来让用户可以自定义转换操作;

加载载网络或本地资源;

picasso图片缓存框架 代码分析
picasso图片缓存框架 代码分析

lrucacha,主要是get和set方法,存储的结构采用了linkedhashmap,这种map内部实现了lru算法(least recently used 近期最少使用算法)。

picasso图片缓存框架 代码分析

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操作后都会判断当前缓存区是否已满,如果满了就清掉最少使用的图形。代码如下

picasso图片缓存框架 代码分析

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++;  

            }  

        }  

}  

picasso图片缓存框架 代码分析
picasso图片缓存框架 代码分析

所有对图形的操作都会记录在这里,供之后图形的创建使用,如重新计算大小,旋转角度,也可以自定义变换,只需要实现transformation,一个bitmap转换的接口。

picasso图片缓存框架 代码分析

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,失败了就将错误通过回调通知到上层。

picasso图片缓存框架 代码分析
picasso图片缓存框架 代码分析

imageviewaction实现了action,在complete中将bitmap和imageview组成了一个picassodrawable,里面会实现淡出的动画效果。

picasso图片缓存框架 代码分析

@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。

picasso图片缓存框架 代码分析

bitmaphunter是一个runnable,其中有一个decode的抽象方法,用于子类实现不同类型资源的解析。

picasso图片缓存框架 代码分析
picasso图片缓存框架 代码分析

    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。

picasso图片缓存框架 代码分析
picasso图片缓存框架 代码分析

dispatcher内有一个handlerthread,所有的请求都会通过这个thread转换,也就是请求也是异步的,这样应该是为了ui线程更加流畅,同时保证请求的顺序,因为handler的消息队列。

外部调用的是dispatchxxx方法,然后通过handler将请求转换到对应的performxxx方法。

例如生成action以后就会调用dispather的dispatchsubmit()来请求执行,

picasso图片缓存框架 代码分析

void dispatchsubmit(action action) {  

        handler.sendmessage(handler.obtainmessage(request_submit, action));  

handler接到消息后转换到performsubmit方法

picasso图片缓存框架 代码分析

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)的过程,

picasso图片缓存框架 代码分析

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执行。