UniversalImageLoader

  简单来说就是用于加载图片的一个开源项目,在其项目介绍中是这么写的

  1. 支持多线程图片加载
  2. 提供丰富的细节配置,比如线程池大小,HTPP请求项,内存和磁盘缓存,图片显示时的参数配置等等;
  3. 提供双缓存
  4. 支持加载过程的监听;
  5. 提供图片的个性化显示配置接口;

  其他类似的项目也有很多,但这个作为github上著名的开源项目被广泛使用。第三方的包虽然好用省力,可以有效避免重复造轮子,但是却隐藏了一些开发上的细节,如果不关注其内部实现,那么将不利于掌握核心技术,当然也谈不上更好的使用它,计划分析项目的集成使用和低层实现。

  源码地址:https://github.com/nostra13/Android-Universal-Image-Loader

简单分析

  1. 加载图片之前,先要做初始化配置,这个类似很多游戏引擎使用前要做一下初始化;
  2. 其实只做了一件事,实例化一个全局的ImageLoader对象,同时传入图片加载缓存的配置
  3. ImageLoaderConfiguration封装了基本的配置信息,比如加载图片事用的线程池大小,线程的优先级,内存缓存大小,是否支持同一图片的多尺寸缓存(默认是支持的,可以手动关闭),还有缓存的命名规则等等.

  这基本也就是几行代码,下面这张图里有实例化和初始化的过程。

  

关于这个实例化,是线程安全,忽略第二层判断,如果A,B线程同时执行if(instance==null),A,B都满足条件进入,此时,其中一个换锁,另一个等待,还需要再次判断instance==null(这是必要的,否则可能使得,再次实例化)这样一个单例就正常初始化了。

  配置完后,就可以开始使用了,通过ImageLoaderdisplayImage()绑定一个图片和ImageView,该方法有四个重载版本,传的参数比较多,这也印证了该项目提供每个图片单独的显示配置这一说法。

  其中参数最全的是:

  1. displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener);

  另外三个其实就是减少其中几个参数用默认的值而已。

  1. String uri, 图片链接没什么疑问
  2. ImageView imageView, 图片载体控件,也没什么好说

  比较重要的是后面两个,DisplayImageOptions options,图片的参数配置对象

  1. options = new DisplayImageOptions.Builder()
  2. .showStubImage(R.drawable.stub_image)
  3. .showImageForEmptyUri(R.drawable.image_for_empty_url)
  4. .cacheInMemory()
  5. .cacheOnDisc()
  6. .build();

  来看看都有什么信息可以配置的,

  1. 第一个是图片加载过程中显示的图片
  2. 第二个是图片加载失败时用的的图片
  3. 第三个允许内存缓存
  4. 第四个允许磁盘缓存

  除此之外还有两个

  1. imageScaleType(ImageScaleType imageScaleType)图片缩放类型
  2. displayer(BitmapDisplayer displayer)bitmap显示控制层,可以在显示图片前对Bitmap简单处理一下,这两个不是一定要设定,应为他们都有默认值。

  最后一个参数ImageLoadingListener listener当然是监听过程的回调接口


Imageloader

  图片加载的Imageloader实现

  

  通过ImageLoader实例对象,调用public void displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener)发放将开始加载图片

  具体过程可以分为几个阶段

合法性检查

  主要是初始化检查和参数检查,可能会

  1. 抛出异常或是下载不受干扰可以继续;有传入的图片地址为空
  2. imageview为空
  3. 图片配置实例为空
  4. 过程监听接口为空四种情况。
  1. if (configuration == null)
  2. {
  3. throw new RuntimeException(ERROR_NOT_INIT);
  4. }
  5. if (imageView == null)
  6. {
  7. Log.w(TAG, ERROR_WRONG_ARGUMENTS);
  8. return;
  9. }
  10. if (listener == null)
  11. {
  12. listener = emptyListener;
  13. }
  14. if (options == null)
  15. {
  16. options = configuration.defaultDisplayImageOptions;
  17. }
  18.  
  19. if (uri == null || uri.length() == 0)
  20. {
  21. cacheKeyForImageView.remove(imageView);
  22. listener.onLoadingStarted();
  23. if (options.isShowImageForEmptyUri())
  24. {
  25. imageView.setImageResource(options.getImageForEmptyUri());
  26. } else
  27. {
  28. imageView.setImageBitmap(null);
  29. }
  30. listener.onLoadingComplete(null);
  31. return;
  32. }
  1. 如果没有初始化ImageLoader是比较严重的,将直接抛出运行时异常
  2. 控件ImageView为空时不影响,可以直接退出,不再下载
  3. 图片配置和监听接口为空则将启用默认值
  4. 至于图片url的话,由于初始化时已经实例化了默认值的情形,所以将显示本地设置的默认图片,同时将控件移出HashMap。

加载准备

  1. 这里的准备操作一个是获取图片的尺寸参数
  2. 然后根据这个参数和url生成标记ImageView的key
  3. 最后以key-value的形式存入HashMap中备用
  1. targetSize = getImageSizeScaleTo(imageView);
  2. String memoryCacheKey = MemoryCacheKeyUtil.generateKey(uri, targetSize);
  3. cacheKeyForImageView.put(imageView, memoryCacheKey);

加载操作

  加载分为调用内存缓存本地缓存/网络下载,根据上一步加载准备中得到的key获取bitmap,这个过程比较发杂

  先看看从内存缓存获取图片

  1. if (bmp != null && !bmp.isRecycled())
  2. {
  3. if (configuration.loggingEnabled)
  4. Log.i(TAG, String.format(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey));
  5. listener.onLoadingStarted();
  6. Bitmap displayedBitmap = options.getDisplayer().display(bmp, imageView);
  7. imageView.setImageBitmap(displayedBitmap);
  8. listener.onLoadingComplete(bmp);
  9. }
  1. 根据得到的bitmap,如果不为空且未被标记为回收状态,那么就可以使用这个缓存的bitmap
  2. 调用监听接口的onLoadingStarted()处理一些加载前的操作,然后对bitmap做一些显示前的操作
  3. 这个就用到传入进来的图片显示的option配置,如果没传这个值,那启用默认值,应该是不对bitmap操作
  4. 这个默认的option使用SimpleBitmapDisplayer 实例
  5. 接下来调用监听接口的onLoadingComlete()
  6. 到此显示内存缓存图片的操作就结束了

  查看其源代码,果然是直接将bitmap设置给ImageView然后对外返回原来的Bitmap,对于这种情况后面的在此设置bitmap给imageview其实有些累赘,重复操作了。

  1. public final class SimpleBitmapDisplayer implements BitmapDisplayer
  2. {
  3. @Override
  4. public Bitmap display(Bitmap bitmap, ImageView imageView)
  5. {
  6. imageView.setImageBitmap(bitmap);
  7. return bitmap;
  8. }
  9. }

  下面介绍第二种情况,就是从磁盘缓存/网新下载图片

  1. 先调用监听接口的onLoadingStarted()
  2. 接着显示一个下载过程中的图片或则干脆在下载时不显示任何图片
  3. 接着检查一下线程池是否初始化并正常工作中checkExecutors()

  查看源代码可以发现,项目用与下载的的task其实是通过ExecutorService来管理

  1. private void checkExecutors()
  2. {
  3. if (imageLoadingExecutor == null || imageLoadingExecutor.isShutdown())
  4. {
  5. imageLoadingExecutor = Executors.newFixedThreadPool(configuration.threadPoolSize, configuration.displayImageThreadFactory);
  6. }
  7. if (cachedImageLoadingExecutor == null || cachedImageLoadingExecutor.isShutdown())
  8. {
  9. cachedImageLoadingExecutor = Executors.newFixedThreadPool(configuration.threadPoolSize, configuration.displayImageThreadFactory);
  10. }
  11. }
  1. 为了下载图片这里把必要的图片信息做了一个封装传给工作线程
  2. ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, options, listener, getLockForUri(uri));
  3. 现在一起看一下他的工作线程是怎么写的:LoadAndDisplayImageTask displayImageTask = new LoadAndDisplayImageTask(configuration, imageLoadingInfo, new Handler());
  4. 这里的最后一个参数是我们熟悉的Handler 实例,可以预测这个task类应该是Runnale的是子类,在run() 中根据情况向handler发送处理操作请求

源码详细注释

  http://blog.csdn.net/wwj_748/article/details/10079311

参考文章

  https://github.com/nostra13/Android-Universal-Image-Loader

  http://blog.csdn.net/wwj_748/article/details/10079311

  http://www.cnblogs.com/avenwu/

【Android】开源项目UniversalImageLoader及开源框架ImageLoader的更多相关文章

  1. Atitit.一些公司的开源项目 重大知名开源项目attilax总结

    Atitit.一些公司的开源项目 重大知名开源项目attilax总结 1. Twitter--Bootstrap:1 2. Google2 2.1. Gson2 2.2. Angular.Js2 2. ...

  2. .NET Core/.NET5/.NET6 开源项目汇总6:框架与架构设计(DDD、云原生/微服务/容器/DevOps/CICD等)项目

    系列目录     [已更新最新开发文章,点击查看详细] 开源项目是众多组织与个人分享的组件或项目,作者付出的心血我们是无法体会的,所以首先大家要心存感激.尊重.请严格遵守每个项目的开源协议后再使用.尊 ...

  3. 如何在Android Studio项目中导入开源库?

    前两天,谷歌发布了Android Studio 1.0的正式版,也有更多的人开始迁移到Android Studio进行开发.然而,网上很多的开源库,控件等还是以前的基于Eclipse进行开发,很多人不 ...

  4. [开源项目-MyBean轻量级配置框架] MyBean的特性和MyBean的开始

    [概述] 自从mBean框架出生后,受到很多朋友的关注,在公司的外包项目中得到了不错的应用.由于mBean是公司的项目,不便开源,于是这几天利用晚上的时间和周末的时间重写了底层beanMananger ...

  5. 开源项目福利-github开源项目免费使用Azure PipeLine

    微软收购Github后,很多人猜想微软可能会砍掉VSTS,然而事实VSTS并没有砍掉,关于Azure Devops的详细信息可以查看 这篇博客,如果想查看原文也可以从链接里提供的原始地址里查看. 今天 ...

  6. 【开源项目13】Volley框架 以及 设置request超时时间

    Volley提供了优美的框架,使android程序网络访问更容易.更快. Volley抽象实现了底层的HTTP Client库,我们不需关注HTTP Client细节,专注于写出更加漂亮.干净的RES ...

  7. [开源项目-MyBean轻量级配置框架] 使用MyBean快速搭建分模块的应用程序(主页面的TAB)(DLL-MDI)

    [概述] 抱歉由于上次开源比较匆忙,没有来的及做一个DEMO,里面也有些垃圾的文件没有及时清理.DEMO其实昨天晚上已经调通.相关说明文档今天晚上才说明好,欢迎大家继续关注和交流,和大家一起分享我10 ...

  8. android开源项目学习

    FBReaderJ FBReaderJ用于Android平台的电子书阅读器,它支持多种电子书籍格式包括:oeb.ePub和fb2.此外还支持直接读取zip.tar和gzip等压缩文档. 项目地址:ht ...

  9. [Android开源项目] GitHub开源项目总结 (转)

    [Android开源项目] GitHub开源项目总结 GitHub开源项目android-styled-dialogs http://neast.cn/forum.php?mod=viewthread ...

随机推荐

  1. linux SSH免密码登录远程服务器

    背景:无密码登录,所谓的无密码登录其实是指通过证书认证的方式登录,使用一种‘公私钥’认证的方式来进行ssh登录 在linux系统中,ssh是远程登录的默认工具,因为该工具的协议使用了RSA/DSA的加 ...

  2. POJ - 1266 -

    题目大意:给出一条圆弧上的两个端点A,B,和圆弧上两端点之间的一个点C,现在要用一块各个定点的坐标均为整数的矩形去覆盖这个圆弧,要求最小的矩形面积. 思路:叉积在本体发挥很强大的作用.首先求出三个点所 ...

  3. RelativeLayout的16种特有属性

    *相对于兄弟控件的位置属性 android:layout_above="@id/center_btn"处于某一个控件的上方 android:layout_below="@ ...

  4. Windows10下 tensorflow-gpu 配置

    引言 越来越多的的人入坑机器学习,深度学习,tensorflow 作为目前十分流行又强大的一个框架,自然会有越来越多的新人(我也刚入门)准备使用,一般装的都是 CPU 版的 tensorflow,然而 ...

  5. go标准库-log包源码学习

    log包是go语言提供的一个简单的日志记录功能,其中定义了一个结构体类型 Logger,是整个包的基础部分,包中的其他方法都是围绕这整个结构体创建的. Logger结构 Logger结构的定义如下: ...

  6. iOS 11开发教程(十九)iOS11应用视图美化按钮之设置按钮的外观

    iOS 11开发教程(十九)iOS11应用视图美化按钮之设置按钮的外观 美化按钮说白了就是对按钮的属性进行设置,设置按钮的属性有两种方法:一种是使用编辑界面中的属性检查器:另一种是使用代码进行设置.以 ...

  7. #1075 : 开锁魔法III

    描述 一日,崔克茜来到小马镇表演魔法. 其中有一个节目是开锁咒:舞台上有 n 个盒子,每个盒子中有一把钥匙,对于每个盒子而言有且仅有一把钥匙能打开它.初始时,崔克茜将会随机地选择 k 个盒子用魔法将它 ...

  8. django——inclusion_tag

    inclusion_tag() 原型: django.template.Library.inclusion_tag() 主要作用:通过渲染一个模板来显示一些数据. 例如,Django的Admin界面使 ...

  9. Springzz中使用监听器,用于容器一启动就加载准备数据(application范围内的数据,用于减轻服务器压力,不用每次都去查数据)

    java代码: public class InitListener implements ServletContextListener { public void contextInitialized ...

  10. bzoj 2013 上升计数

    题意: 给一个数集和一个数d,问满足下列要求的排列数(相同的数要区分):  a[i]+d>=a[i+1] ( i in [1,n) ) 因为数的给出顺序不重要,所以先排序,假如我们已经解决了前i ...