ImageLoader的简单分析(二)

在《ImageLoader的简单分析》这篇博客中对IImageLoader三大组件的创建过程以及三者之间的关系做了说明,同时文章的最后也简单的说明了一下ImageLoader是怎么通过displayImage方法来获取缓存来显示图片的,本文就对ImageLoader的这个知识点做较为详细的说明。

ImageLoader对缓存的使用还是很灵活的:支持同步或者异步加载图片资源,对内存缓存和文件缓存可以选择使用其一或者二者都是用。在构建DisplayImageOptions对象的时候可以通过cacheInMemory(true)、cacheOnDisk(true)两个方法来灵活选择到底使用哪一种缓存策略。另外组件ImageLoaderConfiguration的时候也可配置自定义的内存缓存、文件缓存的具体实现方式。也就是说 ImageLoaderCofiguration的功能之一就是提供了内存缓存和硬件缓存的具体方式配置,但是是否需要对图片进行内存缓存或者文件缓存则是由DisplayImageOptions来决定的。

如何读取使用缓存?

在ImageLoader的实现中如果图片资源Uri不为null的情况下,会先从memory cache里面读取出Bitmap对象,然后使用之,代码如下:

if (options.shouldPostProcess()) {
    ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri,        imageAware, targetSize, memoryCacheKey,options, listener,   progressListener, engine.getLockForUri(uri));
    ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
                        defineHandler(options));
                if (options.isSyncLoading()) {//同步
                    displayTask.run();
                } else {//异步
                    engine.submit(displayTask);
                }
} else {//如果不需要额外的处理,采用Options自定义的Displyer显示SimpleBitmapDisplayer
                options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
                listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
            }

可以看出ImageLoader对Bitmap对象的使用也很灵活:你可以直接从缓存中取出Bitmap对象然后使用之,也可以把读取到的Bitmap设置给ImageView之前做一些处理,比如对bitmap添加特效,增加倒影等操作。然后把特效处理过的Bitmap交给ImageView处理!是否需要对读取的Bitmap处理是由DisplayImageOptions的postProcessor(postProcessor)来决定的,如果在组件DisplayImageOptions的过程中配置了postProcessor,那么上面的代码中if(options.shouldPostProcess())成立。在使用postProcessor的时候你需要传一个BitmapProcessor接口的实现类,这个接口也很简单就是提供了Bitmap process(Bitmap bitmap)方法,方法参数里面的bitmap就是从缓存中读取到的Bitmap对象,该方法返回经过“特效”处理过的Bitmap,然后就交给ImageView处理了,同时对通过process对bitmap的处理也支持同步和异步操作。

1)把ImageView,memoryCache等信息封装成ImageLoadingInfo 对象。

2)把ImageLoadingInfo 对象交给ProcessAndDisplayImageTask,见名知意,这个对象就是使ImageView显示图片的,是一个Ruannable。

3)根据是异步还是同步加载来执行process来处理Bitmap后显示图片,同步与否仍然在DisplayImageOptions类配置,默认是异步。当然异步只是把这个Runnable交给ImageLoader内部的线程来处理,同步则直接运行Ruannable的run方法,所以在此我们直接分析ProcessAndDisplayImageTask的run方法,看看它具体做了什么!

@Override
    public void run() {
        BitmapProcessor processor = imageLoadingInfo.options.getPostProcessor();
        //调用自动以的BitmapProcessor处理器对bitMap进行处理,并将处理过的bitmap交给imageView显示
        Bitmap processedBitmap = processor.process(bitmap);
        DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(processedBitmap, imageLoadingInfo, engine,
                LoadedFrom.MEMORY_CACHE);
        //实际显示图片的地方的地方
        LoadAndDisplayImageTask.runTask(displayBitmapTask, imageLoadingInfo.options.isSyncLoading(), handler, engine);
    }

如上代码所示,ProcessAndDisplayImageTask代码结构也很清晰,具体做了如下三个工作:

1)获取DisplayImageOptions配置的BitmapProcessor 对象(注意是通过getPostProcessor方法获取的,后面还会提到getPreProcessor方法.

2)执行BitmapProcessor 的process方法对Bitmap进行加工处理,并返回之。

3)将加工过的Bitmap传给DisplayBitmapTask对象,该对象也是一个Runnable。

4)将DisplayBitmapTask交给LoadAndDisplayImageTask这个类的runTask方法,并执行;这个runTask方法正是ImageView正式显示图片的方法所在!

那么就让我们看看这个方法又做了些神马:

static void runTask(Runnable r, boolean sync, Handler handler, ImageLoaderEngine engine) {
        //如果是同步
        if (sync) {
            r.run();
        } else if (handler == null) {//ImageLoader开启一个线程加载
            engine.fireCallback(r);
        } else {//如果定义了handler,显示文件的逻辑交给
            handler.post(r);
        }
    }

刚方法也很简单,主要做了如下工作

1)如果是同步的话直接运行DisplayBitmapTask这个Runnable的run方法

2)如果是异步并且你的DisplayImageOptions没有配置handler,就用engine.fireCallback(r);让ImageLoader定义的异步加载机制完成异步操作

3)如果在组件DisplayImageOptions的时候通过handler(Handerl handler)配置了你自己的handler,那么就交给post(r);

在我开发的项目时候倒是没有配置handler那么就说明项目中是异步执行了DisplayBitmapTask这个Runnable,所以有需要分析这个Runnable都做了些神马:

@Override
    public void run() {
        if (imageAware.isCollected()) {//如果被垃圾回收
            listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
        } else if (isViewWasReused()) {
            listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
        } else {
            //实际上是调用SimpleBitmapDisplayer方法
            displayer.display(bitmap, imageAware, loadedFrom);
            engine.cancelDisplayTaskFor(imageAware);
            listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap);
        }
    }

在这里我们直接分析最后的else分支,可以发现ImageLoader最终是通过displayer这个对象的display方法来完成ImageView的图片的显示的,同时让listener执行onLoadingComplete来告知客户端图片已经加载完成,可以在onLoadingComplete方法中做一些额外的处理。

那么这个Displayer对象是何许人也?在DisplayBitmapTask这个类的构造器中是这么对Displayer来完成初始化的:

//DisplayImageOptions的getDisplayer
displayer = imageLoadingInfo.options.getDisplayer();

所以这个对象最初初始化的地方仍然是DisplayImageOptions;那么我们就要看看DisplayImageOptions这个类里面是怎么初始化displayer的。在DisplayImageOptions的构造器有这么段代码:

private final BitmapDisplayer displayer;
DisplayImageOptions(Builder builder){
displayer = builder.displayer;
}

啥都不说了既然是Builder在构建DisplayImageOptions对象的过程中初始化了displayer,而通常在使用Builder组件DisplayImageOptions的过程中基本上不会手动配置BitmapDisplayer 这个对象,所以Builder肯定会默认对其初始化。代码体现如下:

private BitmapDisplayer displayer = DefaultConfigurationFactory.createBitmapDisplayer();

public static BitmapDisplayer createBitmapDisplayer() {
        return new SimpleBitmapDisplayer();
    }

所以最终返回的是SimpleBitmapDisplayer对象,通过该对象的display方法来完成ImageView展示图片的功。所以谜底马上揭晓,让我们看看display方法都干了什么了不起的事儿:

@Override
    public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
        imageAware.setImageBitmap(bitmap);
    }

So easy!就是把生成的Bitmap交给了imageAware对象的setImageBitmap方法来处理!还记得之前说过调用ImageLoader的displayImage(uri,ImageView)方法会把ImageView会把包装成ImageViewAware对象,那么此处的imageAware正式我们的目标ImageView!!!所以既然都到这了,还是乖乖看setImageBitmap方法的具体实现吧!

//此方法在ImageViewAware的父类ViewAware中。
    public boolean setImageBitmap(Bitmap bitmap) {
         //回到我们熟悉的UI线程啦
        if (Looper.myLooper() == Looper.getMainLooper()) {
            View view = viewRef.get();
            if (view != null) {
                //调用setImageBitmapInto方法来最终显示
                setImageBitmapInto(bitmap, view);
                return true;
            }
        } else {
            L.w(WARN_CANT_SET_BITMAP);
        }
        return false;
    }

我屮艸芔茻!!!怎么还没看到ImageView怎么显示的呢?最终又要调用setImageBitmapInto,日了狗了,有种放长线掉到了大鱼的感觉:

    @Override
    protected void setImageBitmapInto(Bitmap bitmap, View view) {
        ((ImageView) view).setImageBitmap(bitmap);
    }

souga,最终就是把调用了ImageView的setImageBitmap方法而已!

啰嗦了一大堆还是对ImageView从内存缓存中获取Bitmap并显示出来的过程总结比较好:

1)从ImageLoader的memory cache中读取出Bitmap对象

2)判断是否需要对生成的Bitmap对象做处理,如果不需要跳到步骤4,否则执行步骤3

3)是否是异步操作,如果是异步操作则开启ImageLoader的内部异步机制完成了对bitmap处理后跳转到步骤4,否则同步对bitmap处理后跳转到步骤4

4)用SimpleBitmapDisplayer对象的display方法最终调用ImageView的setImageBitmap来展示图像。

最后用流程图来直观表示就是如下所示了:

到此为止,本篇博客结束,至于对文件缓存以及下载网络图片资源的流程限于篇幅,将另外开一篇博客来做说明,如有错误不当之处,欢迎批评指正。

时间: 2024-10-09 21:26:43

ImageLoader的简单分析(二)的相关文章

ImageLoader的简单分析

刚接触android的时候看到项目里面用到了ImageLoader这个图片缓存插件,当初抱着"知其然必要知其所以然"的想法还专门下载了它的源码没头苍蝇似的毫无章法的去看了看,当然最终因为对android相关知识的了解有限而作罢.本篇也不会细致深入的对此多做说明,只是对ImageLoader的创建过程等略作梳理,方便以后的使用. 在这里,从使用的源头开始说起慢慢抽丝拨茧,ImagerLoader提供了如下的来加载展示图片的方法: 通常直接会使用上图中的前五个来,至于的五个接口十八Imag

ImageLoader简单分析(三)

其实对于缓存的实现原理及其流程总的来说都很简单,无非就是先从网络加载相关资源,然后用内存缓存或者磁盘缓存把下载到的资源缓存起来:等再次加载相同的资源的时候如果内存缓存或者磁盘缓存还存在就用缓存里面的资源,否则仍然进行网络加载,重复此过程而已.严格说来也没什么可讲的,但是通过研读ImageLoader的源码倒是可以学到很多缓存之外的东西:学学别人的代码怎么设计,资源加载的异步处理机制的灵活使用等等,甚至也可以吸取到一些东西来运用到自己的项目中去.就ImageLoader本身来说,也可以让人了解其工

网口扫盲二:Mac与Phy组成原理的简单分析

1. general 下图是网口结构简图.网口由CPU.MAC和PHY三部分组成.DMA控制器通常属于CPU的一部分,用虚线放在这里是为了表示DMA控制器可能会参与到网口数据传输中. 对于上述的三部分,并不一定都是独立的芯片,根据组合形式,可分为下列几种类型: CPU集成MAC与PHY; CPU集成MAC,PHY采用独立芯片; CPU不集成MAC与PHY,MAC与PHY采用集成芯片; 本例中选用方案二做进一步说明,因为CPU总线接口很常见,通常都会做成可以像访问内存一样去访问,没必要拿出来说,而

62. ImageLoader源代码-流程分析

一. ImageLoader简介 Android library #1 on GitHub. UIL aims to provide a powerful, flexible and highly customizable instrument for image loading, caching and displaying. It provides a lot of configuration options and good control over the image loading a

实时计算,流数据处理系统简介与简单分析

转自:http://www.csdn.net/article/2014-06-12/2820196-Storm 摘要:实时计算一般都是针对海量数据进行的,一般要求为秒级.实时计算主要分为两块:数据的实时入库.数据的实时计算.今天这篇文章详细介绍了实时计算,流数据处理系统简介与简单分析. 编者按:互联网领域的实时计算一般都是针对海量数据进行的,除了像非实时计算的需求(如计算结果准确)以外,实时计算最重要的一个需求是能够实时响应计算结果,一般要求为秒级.实时计算的今天,业界都没有一个准确的定义,什么

java基础---->hashSet的简单分析(一)

对于HashSet而言,它是基于HashMap实现的,底层采用HashMap来保存元素的.今天我们就简单的分析一下它的实现. HashSet的简单分析 一.hashSet的成员变量组成 public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable private transient HashMap<E,Object> map;

[转载]通达信插件选股(基于通达信插件编程规范的简单分析)

[转载]通达信插件选股(基于通达信插件编程规范的简单分析) 原文地址:通达信插件选股(基于通达信插件编程规范的简单分析)作者:哈尔滨猫猫 首先声明,鄙人是编程人员,不是股民.对选股概念了解甚少.本文仅作编程人员学习借鉴之用.不对选股理论进行探讨和解释. 以前有客户找我做过通达信插件选股的小任务,当时第一次接触面向接口(此类“接口”)编程,也是第一次接触到股票里相关的概念.最近由于接手一个任务,与上次开发相类似,故旧事重提,仔细研究一番.将个人学习研究所得知识与大家分享.在网上搜相关资料,可用的.

Redis数据持久化机制AOF原理分析二

Redis数据持久化机制AOF原理分析二 分类: Redis 2014-01-12 15:36  737人阅读  评论(0)  收藏  举报 redis AOF rewrite 目录(?)[+] 本文所引用的源码全部来自Redis2.8.2版本. Redis AOF数据持久化机制的实现相关代码是redis.c, redis.h, aof.c, bio.c, rio.c, config.c 在阅读本文之前请先阅读Redis数据持久化机制AOF原理分析之配置详解文章,了解AOF相关参数的解析,文章链

[Android]Volley源码分析(二)Cache

Cache作为Volley最为核心的一部分,Volley花了重彩来实现它.本章我们顺着Volley的源码思路往下,来看下Volley对Cache的处理逻辑. 我们回想一下昨天的简单代码,我们的入口是从构造一个Request队列开始的,而我们并不直接调用new来构造,而是将控制权反转给Volley这个静态工厂来构造. com.android.volley.toolbox.Volley: public static RequestQueue newRequestQueue(Context conte