ImageLoader的简单分析

来源:互联网 发布:曲阜问政网络平台 编辑:程序博客网 时间:2024/05/18 00:03

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

    public void displayImage(String uri, ImageView imageView, DisplayImageOptions options) {        displayImage(uri, new ImageViewAware(imageView), options, null, null);    }

就是把imageView封装成ImageViewAware后传给了如下方法:

public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,            ImageLoadingListener listener, ImageLoadingProgressListener progressListener) 

所以直接分析上面的方法就可以!
进入displayImage这个参数最多的方法,其实上图中的其他重载方法都会调用这个方法,该方法首先会执行checkConfiguration();方法来检测configuration是否为null,如果为null就抛出异常。

private void checkConfiguration() {        if (configuration == null) {            throw new IllegalStateException(ERROR_NOT_INIT);        }    }

ImageLoaderConfiguration的创建

那么这个configuration是什么呢?其实就是ImageLoaderConfiguration,其实用过ImageLoader的都应该清楚,我们在使用ImageLoader的时候首先就要在某一个地方比如Application的onCreate方法里面配置ImageLoaderConfiguration,用来初始化ImageLoader的一些配置参数:诸如缓存策略、可以缓存的文件数量等。
简单的配置代码如下:

ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(mContext)                .threadPoolSize(Thread.NORM_PRIORITY - 2)                 .threadPriority(Thread.NORM_PRIORITY - 2)                 .memoryCache(new WeakMemoryCache())                .memoryCacheSize(2 * 1024 * 1024)                .memoryCacheSizePercentage(13) // default                .diskCache(new UnlimitedDiscCache(cacheDir)                .build(); // 开始构建ImageLoader.getInstance().init(config);

这明显是一个Builder模式的应用,Builder模式的好处之一就是可以对比较复杂的对象逐步创建一个个小组件,然后由这些小组件最终完成复杂对象的初始化(构建)。上面代码通过Builder这个类来一步步创建ImageLoaderConfiguration这个类所需的一些参数之后,最终会通过build()方法来完成ImageLoaderConfiguation对象的创建!至于构建模式的说明读者可以参考网上的一些介绍。
其实build()方面也很简单:

public ImageLoaderConfiguration build() {            initEmptyFieldsWithDefaultValues();            //构造方法里面的this,指的就是Builder对象            return new ImageLoaderConfiguration(this);        }

build方法做了两个工作:
1)通过initEmptyFieldsWithDefaultValues()方法来设置一些默认的参数(前提是如果你通过Builder构建ImageLoaderConfiguation这个对象的过程中没有配置ImageLoaderConfiguation所需的参数),比如在Build的过程中没有调用Builder的imageDownloader方法来配置自己的下载图片的逻辑,那么在initEmptyFieldsWithDefaultValues方法中就会采用ImageLoader默认的下载方式来进行图片的下载:

    if (downloader == null) {                downloader = DefaultConfigurationFactory.createImageDownloader(context);            }

2)返回ImageLoaderConfiguration对象。
ImageLoaderConfiguration的构造参数也很简单,就是把Builder对象构建的一个个组件设置给ImageLoaderConfiguration!该类的构造器是private的,切ImageLoaderConfiguration类是final的,而且它所持有的所有属性也是final的,一旦build完毕就不可更改!

private ImageLoaderConfiguration(final Builder builder) {        resources = builder.context.getResources();        maxImageWidthForMemoryCache = builder.maxImageWidthForMemoryCache;        maxImageHeightForMemoryCache = builder.maxImageHeightForMemoryCache;        maxImageWidthForDiskCache = builder.maxImageWidthForDiskCache;        maxImageHeightForDiskCache = builder.maxImageHeightForDiskCache;        processorForDiskCache = builder.processorForDiskCache;        taskExecutor = builder.taskExecutor;        taskExecutorForCachedImages = builder.taskExecutorForCachedImages;        threadPoolSize = builder.threadPoolSize;        threadPriority = builder.threadPriority;        tasksProcessingType = builder.tasksProcessingType;        diskCache = builder.diskCache;        memoryCache = builder.memoryCache;        defaultDisplayImageOptions = builder.defaultDisplayImageOptions;        downloader = builder.downloader;        decoder = builder.decoder;        customExecutor = builder.customExecutor;        customExecutorForCachedImages = builder.customExecutorForCachedImages;        networkDeniedDownloader = new NetworkDeniedImageDownloader(downloader);        slowNetworkDownloader = new SlowNetworkImageDownloader(downloader);    }

DisplayImageOptions的创建和使用

上面主要讲了checkConfiguration()以及ImageLoaderConfiguation的构建过程。下面接着对displayImage方法的说明。接着就是我们非常熟悉的DisplayImageOptions的使用了,如果用户自己在使用displayImage一系列重载方法的时候没有传自定义的DisplayImageOptions对象,那么就会使用默认的DisplayImageOptions对象:

if (options == null) {  //configuration为上文所说的ImageLoaderConfiguration对象。            options = configuration.defaultDisplayImageOptions;        }

`这个默认的DisplayImageOptions是又Builder创建的ImageLoaderConfiguration来初始化的,代码如下:

final DisplayImageOptions defaultDisplayImageOptions;private ImageLoaderConfiguration(final Builder builder) {       //此处省略了若干代码    defaultDisplayImageOptions = builder.defaultDisplayImageOptions;

正如上面代码所示DisplayImageOptions类里面的defaultDisplayImageOptions引用是由builder构建过来的!你可以通过Builder类的defaultDisplayImageOptions方法来完成对defaultDisplayImageOptions的初始化工作:

public Builder defaultDisplayImageOptions(DisplayImageOptions defaultDisplayImageOptions) {            this.defaultDisplayImageOptions = defaultDisplayImageOptions;            return this;        }

那么问题来了?如果在创建Builder的时候如果没有调用defaultDisplayImageOptions()方法设置options岂不是为null?还记得上文build()方法里面的调用的initEmptyFieldsWithDefaultValues()么?这个方法就是:防止在使用Builder创建ImageLoaderConfiguration的时候客户端没有配置一些ImageLoader需要的组件而默认用ImageLoader一些组件。比如如果你的DisplayImageOptions没有在客户端指定,那么在initEmptyFieldsWithDefaultValues()里面有如下代码确保了defaultDisplayImageOptions不为null:

if (defaultDisplayImageOptions == null) {                defaultDisplayImageOptions = DisplayImageOptions.createSimple();            }    public static DisplayImageOptions createSimple() {        return new Builder().build();    }

其实上面的代码createSimple也是Builder模式的应用。到此ImageLoaderConfiguration的创建才算最终完成了,那么如何把这个对象叫给ImageLoader来使用呢?

ImageLoader的工作细节梳理

ImageLoader是一个单例类,提供了如下方式来完成了ImageLoader的初始化工作:

Imageloader.getInstance().init(ImageLoaderConfiguration)

既然是通过init方法来完成ImageLoader与ImageLoaderCOnfiguration的关联,那么就让我们看看init方法都做了些神马?

public synchronized void init(ImageLoaderConfiguration configuration) {        if (configuration == null) {            throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL);        }        if (this.configuration == null) {            engine = new ImageLoaderEngine(configuration);            this.configuration = configuration;        } else {        }    }

也很简单,就是把configuration简单的赋予了ImageLoader.configuration引用!到此处ImageLoader初始化也完毕了,同样ImageLoaderConfiguration也初始化完毕了!所以就可以放心的调用ImageLoader.getInstance().displayImage来完成工作了!
在这里说一个可能不是技巧的技巧:在用DisplayImageOptions来控制页面显示样式的时候如果你的应用里面的图片样式:比如失败的图片,加载过程中的图片等都许多相似的地方,那么你就可以通过Builder的defaultDisplayImageOptions方法来手动指定默认的Options,个别页面如果需要不同的风格,就可以调用displayImage的其他重载方法传入具体通过DisplayImageOptions的Builder构建Options的对象就可以了,简单的配置代码如下:

private void initImageLoader(Context context) {        DisplayImageOptions options = new     DisplayImageOptions.Builder().build();        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context).        defaultDisplayImageOptions(options)//把上面创建的options交给ImageLoaderConfiguration        .build();    ImageLoader.getInstance().init(config);    }

ImageLodaer对URL为null的处理

在前面的一系列说明之后,终于进入的Imageloader的核心工作:展示图片!

  • 如果图片资源地址为null的情况:处理逻辑很简单,代码如下:
if (TextUtils.isEmpty(uri)) {            engine.cancelDisplayTaskFor(imageAware);            listener.onLoadingStarted(uri, imageAware.getWrappedView());            if (options.shouldShowImageForEmptyUri()) {                imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));            } else {//没有指定emptyUri的情况                imageAware.setImageDrawable(null);            }            listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);            return;        }

如果在配置DisplayImageOptions的时候通过它的Builder调用了showImageForEmptyUri方法,那么就让ImageView显示showImageForEmptyUri指定的那个图片资源,否则就神马都不显示 如果阅读仔细的话,上面也提供了加载图片的监听listener.onLoadingStarted,listener.onLoadingComplete,你也可以调用displayImage相关重载方法传入自己的ImageLoadingListener对象,否则ImageLoader会调用自己的默认实现。
图片资源非null的情况,那就很简单了,就是使用缓存的思路了:先从缓存中读取,如果缓存存在就用缓存中的图片资源,否则就下载新的资源并进行缓存!代码如下:

//1.对缓存进行一些配置         ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());        String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);        engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);        //2.开始加载图片监听        listener.onLoadingStarted(uri, imageAware.getWrappedView());        //3.从内存缓存中获取Bitmap         Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);        if (bmp != null && !bmp.isRecycled()) {//缓存存在            if (options.shouldPostProcess()) {//如果对DisplayImageOptions设置了BitmapProcessor                ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,                        options, listener, progressListener, engine.getLockForUri(uri));                //开启一个Runnable                ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,                        defineHandler(options));                if (options.isSyncLoading()) {//同步执行显示图片逻辑                    displayTask.run();                } else {//异步执行显示图片逻辑                    engine.submit(displayTask);                }            } else {//如果没有配置BitmapProcessor                //通过BitmapDisplayer来完成显示图片的逻辑,该Displayer如果没指定的话就是用DefaultConfigurationFactory.createBitmapDisplayer();                options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);                listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);            }        } else {//如果缓存不存在执行文件缓存或者从网络加载            if (options.shouldShowImageOnLoading()) {//添加下载过程中的图片                imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));            } else if (options.isResetViewBeforeLoading()) {                imageAware.setImageDrawable(null);            }            ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,                    options, listener, progressListener, engine.getLockForUri(uri));            //初始化一个下载的Runnable            LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,                    defineHandler(options));            if (options.isSyncLoading()) {//同步处理下载                displayTask.run();            } else {//异步下载图片                engine.submit(displayTask);            }        }

上面代码中的注释说明了各个模块的作用,关于详细的内容限于篇幅以及时间太晚就另外开一篇博客进行说明。
总结一下本博客的要点:
1)ImageLoader的使用先通过Builder模式构建ImageLoaderConfiguration对象
2)通过Builder模式一步步创建DisplayImageOptions对象。
3)通过ImageLoader的init方法把ImageLoaderConfiguration对象和ImageLoader对象关联起来,当然也使得ImageLoader也关联了DisplayImageOptions对象
4)通过ImageLoader的displayImage重载方法,结合ImageLoaderConfiguration对象和DisplayImageOptions对象完成了对ImageView的展示图片的功能。
简单的用图片表示其三者之间的关系:
这里写图片描述
图片画的有点丑,不过也凑合,本篇博客到此为止,如有不当之处欢迎批评指正共同学习和提高。

4 0
原创粉丝点击