ImageLoader的简单分析
2016-07-12 23:22
225 查看
刚接触android的时候看到项目里面用到了ImageLoader这个图片缓存插件,当初抱着“知其然必要知其所以然”的想法还专门下载了它的源码没头苍蝇似的毫无章法的去看了看,当然最终因为对android相关知识的了解有限而作罢。本篇也不会细致深入的对此多做说明,只是对ImageLoader的创建过程等略作梳理,方便以后的使用。
在这里,从使用的源头开始说起慢慢抽丝拨茧,ImagerLoader提供了如下的来加载展示图片的方法:
通常直接会使用上图中的前五个来,至于的五个接口十八ImageView先包装秤ImageAware来使用。
在使用上图中的前五个方法的时候,简单查看源码:
就是把imageView封装成ImageViewAware后传给了如下方法:
所以直接分析上面的方法就可以!
进入displayImage这个参数最多的方法,其实上图中的其他重载方法都会调用这个方法,该方法首先会执行checkConfiguration();方法来检测configuration是否为null,如果为null就抛出异常。
简单的配置代码如下:
这明显是一个Builder模式的应用,Builder模式的好处之一就是可以对比较复杂的对象逐步创建一个个小组件,然后由这些小组件最终完成复杂对象的初始化(构建)。上面代码通过Builder这个类来一步步创建ImageLoaderConfiguration这个类所需的一些参数之后,最终会通过build()方法来完成ImageLoaderConfiguation对象的创建!至于构建模式的说明读者可以参考网上的一些介绍。
其实build()方面也很简单:
build方法做了两个工作:
1)通过initEmptyFieldsWithDefaultValues()方法来设置一些默认的参数(前提是如果你通过Builder构建ImageLoaderConfiguation这个对象的过程中没有配置ImageLoaderConfiguation所需的参数),比如在Build的过程中没有调用Builder的imageDownloader方法来配置自己的下载图片的逻辑,那么在initEmptyFieldsWithDefaultValues方法中就会采用ImageLoader默认的下载方式来进行图片的下载:
2)返回ImageLoaderConfiguration对象。
ImageLoaderConfiguration的构造参数也很简单,就是把Builder对象构建的一个个组件设置给ImageLoaderConfiguration!该类的构造器是private的,切ImageLoaderConfiguration类是final的,而且它所持有的所有属性也是final的,一旦build完毕就不可更改!
`这个默认的DisplayImageOptions是又Builder创建的ImageLoaderConfiguration来初始化的,代码如下:
正如上面代码所示DisplayImageOptions类里面的defaultDisplayImageOptions引用是由builder构建过来的!你可以通过Builder类的defaultDisplayImageOptions方法来完成对defaultDisplayImageOptions的初始化工作:
那么问题来了?如果在创建Builder的时候如果没有调用defaultDisplayImageOptions()方法设置options岂不是为null?还记得上文build()方法里面的调用的initEmptyFieldsWithDefaultValues()么?这个方法就是:防止在使用Builder创建ImageLoaderConfiguration的时候客户端没有配置一些ImageLoader需要的组件而默认用ImageLoader一些组件。比如如果你的DisplayImageOptions没有在客户端指定,那么在initEmptyFieldsWithDefaultValues()里面有如下代码确保了defaultDisplayImageOptions不为null:
其实上面的代码createSimple也是Builder模式的应用。到此ImageLoaderConfiguration的创建才算最终完成了,那么如何把这个对象叫给ImageLoader来使用呢?
既然是通过init方法来完成ImageLoader与ImageLoaderCOnfiguration的关联,那么就让我们看看init方法都做了些神马?
也很简单,就是把configuration简单的赋予了ImageLoader.configuration引用!到此处ImageLoader初始化也完毕了,同样ImageLoaderConfiguration也初始化完毕了!所以就可以放心的调用ImageLoader.getInstance().displayImage来完成工作了!
在这里说一个可能不是技巧的技巧:在用DisplayImageOptions来控制页面显示样式的时候如果你的应用里面的图片样式:比如失败的图片,加载过程中的图片等都许多相似的地方,那么你就可以通过Builder的defaultDisplayImageOptions方法来手动指定默认的Options,个别页面如果需要不同的风格,就可以调用displayImage的其他重载方法传入具体通过DisplayImageOptions的Builder构建Options的对象就可以了,简单的配置代码如下:
如果图片资源地址为null的情况:处理逻辑很简单,代码如下:
如果在配置DisplayImageOptions的时候通过它的Builder调用了showImageForEmptyUri方法,那么就让ImageView显示showImageForEmptyUri指定的那个图片资源,否则就神马都不显示 如果阅读仔细的话,上面也提供了加载图片的监听listener.onLoadingStarted,listener.onLoadingComplete,你也可以调用displayImage相关重载方法传入自己的ImageLoadingListener对象,否则ImageLoader会调用自己的默认实现。
图片资源非null的情况,那就很简单了,就是使用缓存的思路了:先从缓存中读取,如果缓存存在就用缓存中的图片资源,否则就下载新的资源并进行缓存!代码如下:
上面代码中的注释说明了各个模块的作用,关于详细的内容限于篇幅以及时间太晚就另外开一篇博客进行说明。
总结一下本博客的要点:
1)ImageLoader的使用先通过Builder模式构建ImageLoaderConfiguration对象
2)通过Builder模式一步步创建DisplayImageOptions对象。
3)通过ImageLoader的init方法把ImageLoaderConfiguration对象和ImageLoader对象关联起来,当然也使得ImageLoader也关联了DisplayImageOptions对象
4)通过ImageLoader的displayImage重载方法,结合ImageLoaderConfiguration对象和DisplayImageOptions对象完成了对ImageView的展示图片的功能。
简单的用图片表示其三者之间的关系:
图片画的有点丑,不过也凑合,本篇博客到此为止,如有不当之处欢迎批评指正共同学习和提高。
在这里,从使用的源头开始说起慢慢抽丝拨茧,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的展示图片的功能。
简单的用图片表示其三者之间的关系:
图片画的有点丑,不过也凑合,本篇博客到此为止,如有不当之处欢迎批评指正共同学习和提高。
相关文章推荐
- 分布式系统理论进阶 - Paxos
- 集合(五)不正确地使用HashMap引发死循环及元素丢失
- 三个简单工厂实例 - C++实现
- OpenGL入门记录--“太阳,地球和月亮”系统代码
- 数据结构---图---邻接表
- iOS学习(三十三)使用NSUserDefaults简单的登陆界面保存用户名,下次启动并显示出来
- HTTP应答状态
- SwipeRefreshLayout + RecyclerView实现上拉加载和下拉刷新
- PHP文件上传操作
- 初次动手编写驱动——LED在linux中的驱动。
- android View属性之dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent
- mysql 查看并修改默认端口号
- BZOJ 2157 旅游【裸链剖+线段树
- GDAL2.1 C#编译
- uva10129(play with words)
- 华为OJ 初级:自首数
- HTML中样式CSS控制基础
- 两个数组的交集
- 基于VFW视频采集及数据处理
- 基于注解ssh的通用dao的crud