文档章节

Android Universal Image Loader 源码分析(五)

k
 kim366
发布于 2016/05/13 19:15
字数 2645
阅读 1
收藏 0
4.2.41 UnlimitedDiskCache.java

一个无大小限制的本地图片缓存。与BaseDiskCache无异,只是用了个意思明确的类名。

4.2.42 DiskLruCache.java

限制总字节大小的内存缓存,会在缓存满时优先删除最近最少使用的元素。

通过缓存目录下名为journal的文件记录缓存的所有操作,并在缓存open时读取journal的文件内容存储到LinkedHashMap<String, Entry> lruEntries中,后面get(String key)获取缓存内容时,会先从lruEntries中得到图片文件名返回文件。

LRU 的实现跟上面内存缓存类似,lruEntriesnew LinkedHashMap<String, Entry>(0, 0.75f, true),LinkedHashMap 第三个参数表示是否需要根据访问顺序(accessOrder)排序,true 表示根据accessOrder排序,最近访问的跟最新加入的一样放到最后面,false 表示根据插入顺序排序。这里为 true 且缓存满时trimToSize()函数始终删除第一个元素,即始终删除最近最少访问的文件。

来源于 JakeWharton 的开源项目 DiskLruCache,具体分析请等待 DiskLruCache 源码解析 完成。

4.2.43 LruDiskCache.java

限制总字节大小的内存缓存,会在缓存满时优先删除最近最少使用的元素,实现了DiskCache
内部有个DiskLruCache cache属性,缓存的存、取操作基本都是由该属性代理完成。

4.2.44 StrictLineReader.java

通过readLine()函数从InputStream中读取一行,目前仅用于磁盘缓存操作记录文件journal的解析。

4.2.45 Util.java

工具类。
String readFully(Reader reader)读取 reader 中内容。
deleteContents(File dir)递归删除文件夹内容。

4.2.46 ContentLengthInputStream.java

InputStream的装饰者,可通过available()函数得到 InputStream 对应数据源的长度(总字节数)。主要用于计算文件存储进度即图片下载进度时的总进度。

4.2.47 FailReason.java

图片下载及显示时的错误原因,目前包括:
IO_ERROR 网络连接或是磁盘存储错误。
DECODING_ERROR decode image 为 Bitmap 时错误。
NETWORK_DENIED 当图片不在缓存中,且设置不允许访问网络时的错误。
OUT_OF_MEMORY 内存溢出错误。
UNKNOWN 未知错误。

4.2.48 FlushedInputStream.java

为了解决早期 Android 版本BitmapFactory.decodeStream(…)在慢网络情况下 decode image 异常的 Bug。
主要通过重写FilterInputStream的 skip(long n) 函数解决,确保 skip(long n) 始终跳过了 n 个字节。如果返回结果即跳过的字节数小于 n,则不断循环直到 skip(long n) 跳过 n 字节或到达文件尾。

4.2.49 ImageScaleType.java

Image 的缩放类型,目前包括:
NONE不缩放。
NONE_SAFE根据需要以整数倍缩小图片,使得其尺寸不超过 Texture 可接受最大尺寸。
IN_SAMPLE_POWER_OF_2根据需要以 2 的 n 次幂缩小图片,使其尺寸不超过目标大小,比较快的缩小方式。
IN_SAMPLE_INT根据需要以整数倍缩小图片,使其尺寸不超过目标大小。
EXACTLY根据需要缩小图片到宽或高有一个与目标尺寸一致。
EXACTLY_STRETCHED根据需要缩放图片到宽或高有一个与目标尺寸一致。

4.2.50 ViewScaleType.java

ImageAware的 ScaleType。
将 ImageView 的 ScaleType 简化为两种FIT_INSIDECROP两种。FIT_INSIDE表示将图片缩放到至少宽度和高度有一个小于等于 View 的对应尺寸,CROP表示将图片缩放到宽度和高度都大于等于 View 的对应尺寸。

4.2.51 ImageSize.java

表示图片宽高的类。
scaleDown(…) 等比缩小宽高。
scale(…) 等比放大宽高。

4.2.52 LoadedFrom.java

图片来源枚举类,包括网络、磁盘缓存、内存缓存。

4.2.53 ImageDecoder.java

将图片转换为 Bitmap 的接口,抽象函数:

Bitmap decode(ImageDecodingInfo imageDecodingInfo) throws IOException;

表示根据ImageDecodingInfo信息得到图片并根据参数将其转换为 Bitmap。

4.2.54 BaseImageDecoder.java

实现了ImageDecoder。调用ImageDownloader获取图片,然后根据ImageDecodingInfo或图片 Exif 信息处理图片转换为 Bitmap。

主要函数:

(1). decode(ImageDecodingInfo decodingInfo)

调用ImageDownloader获取图片,再调用defineImageSizeAndRotation(…)函数得到图片的相关信息,调用prepareDecodingOptions(…)得到图片缩放的比例,调用BitmapFactory.decodeStream将 InputStream 转换为 Bitmap,最后调用considerExactScaleAndOrientatiton(…)根据参数将图片放大、翻转、旋转为合适的样子返回。

(2). defineImageSizeAndRotation(InputStream imageStream, ImageDecodingInfo decodingInfo)

得到图片真实大小以及 Exif 信息(设置考虑 Exif 的条件下)。

(3). defineExifOrientation(String imageUri)

得到图片 Exif 信息中的翻转以及旋转角度信息。

(4). prepareDecodingOptions(ImageSize imageSize, ImageDecodingInfo decodingInfo)

得到图片缩放的比例。

  1. 如果scaleType等于ImageScaleType.NONE,则缩放比例为 1;
  2. 如果scaleType等于ImageScaleType.NONE_SAFE,则缩放比例为 (int)Math.ceil(Math.max((float)srcWidth / maxWidth, (float)srcHeight / maxHeight))
  3. 否则,调用ImageSizeUtils.computeImageSampleSize(…)计算缩放比例。
    在 computeImageSampleSize(…) 中
  4. 如果viewScaleType等于ViewScaleType.FIT_INSIDE
    1.1 如果scaleType等于ImageScaleType.IN_SAMPLE_POWER_OF_2,则缩放比例从 1 开始不断 *2 直到宽或高小于最大尺寸;
    1.2 否则取宽和高分别与最大尺寸比例中较大值,即Math.max(srcWidth / targetWidth, srcHeight / targetHeight)
  5. 如果scaleType等于ViewScaleType.CROP
    2.1 如果scaleType等于ImageScaleType.IN_SAMPLE_POWER_OF_2,则缩放比例从 1 开始不断 *2 直到宽和高都小于最大尺寸。
    2.2 否则取宽和高分别与最大尺寸比例中较小值,即Math.min(srcWidth / targetWidth, srcHeight / targetHeight)
  6. 最后判断宽和高是否超过最大值,如果是 *2 或是 +1 缩放。
(5). considerExactScaleAndOrientatiton(Bitmap subsampledBitmap, ImageDecodingInfo decodingInfo, int rotation, boolean flipHorizontal)

根据参数将图片放大、翻转、旋转为合适的样子返回。

4.2.55 ImageDecodingInfo.java

Image Decode 需要的信息。
String imageKey 图片。
String imageUri 图片 uri,可能是缓存文件的 uri。
String originalImageUri 图片原 uri。
ImageSize targetSize 图片的显示尺寸。
imageScaleType 图片的 ScaleType。
ImageDownloader downloader 图片的下载器。
Object extraForDownloader 下载器需要的辅助信息。
boolean considerExifParams 是否需要考虑图片 Exif 信息。
Options decodingOptions 图片的解码信息,为 BitmapFactory.Options。

4.2.56 BitmapDisplayer.java

ImageAware中显示 bitmap 对象的接口。可在实现中对 bitmap 做一些额外处理,比如加圆角、动画效果。

4.2.57 FadeInBitmapDisplayer.java

图片淡入方式显示在ImageAware中,实现了BitmapDisplayer接口。

4.2.58 RoundedBitmapDisplayer.java

为图片添加圆角显示在ImageAware中,实现了BitmapDisplayer接口。主要通过BitmapShader实现。

4.2.59 RoundedVignetteBitmapDisplayer.java

为图片添加渐变效果的圆角显示在ImageAware中,实现了BitmapDisplayer接口。主要通过RadialGradient实现。

4.2.60 SimpleBitmapDisplayer.java

直接将图片显示在ImageAware中,实现了BitmapDisplayer接口。

4.2.61 BitmapProcessor.java

图片处理接口。可用于对图片预处理(Pre-process Bitmap)和后处理(Post-process Bitmap)。抽象函数:

public interface BitmapProcessor {
    Bitmap process(Bitmap bitmap);
}

用户可以根据自己需求去实现它。比如你想要为你的图片添加一个水印,那么可以自己去实现 BitmapProcessor 接口,在DisplayImageOptions中配置 Pre-process 阶段预处理图片,这样设置后存储在文件系统以及内存缓存中的图片都是加了水印后的。如果只希望在显示时改变不动原图片,可以在BitmapDisplayer中处理。

4.2.62 PauseOnScrollListener.java

可在 View 滚动过程中暂停图片加载的 Listener,实现了 OnScrollListener 接口。
它的好处是防止滚动中不必要的图片加载,比如快速滚动不希望滚动中的图片加载。在 ListView 或 GridView 中 item 加载图片最好使用它,简单的一行代码:

gridView.setOnScrollListener(new PauseOnScrollListener(ImageLoader.getInstance(), false, true));

主要的成员变量:
pauseOnScroll 触摸滑动(手指依然在屏幕上)过程中是否暂停图片加载。
pauseOnFling 甩指滚动(手指已离开屏幕)过程中是否暂停图片加载。
externalListener 自定义的 OnScrollListener 接口,适用于 View 原来就有自定义 OnScrollListener 情况设置。

实现原理:
重写onScrollStateChanged(…)函数判断不同的状态下暂停或继续图片加载。
OnScrollListener.SCROLL_STATE_IDLE表示 View 处于空闲状态,没有在滚动,这时候会加载图片。
OnScrollListener.SCROLL_STATE_TOUCH_SCROLL表示 View 处于触摸滑动状态,手指依然在屏幕上,通过pauseOnScroll变量确定是否需要暂停图片加载。这种时候大都属于慢速滚动浏览状态,所以建议继续图片加载。
OnScrollListener.SCROLL_STATE_FLING表示 View 处于甩指滚动状态,手指已离开屏幕,通过pauseOnFling变量确定是否需要暂停图片加载。这种时候大都属于快速滚动状态,所以建议暂停图片加载以节省资源。

4.2.63 QueueProcessingType.java

任务队列的处理类型,包括FIFO先进先出、LIFO后进先出。

4.2.64 LIFOLinkedBlockingDeque.java

后进先出阻塞队列。重写LinkedBlockingDequeoffer(…)函数如下:

@Override
public boolean offer(T e) {
    return super.offerFirst(e);
}

LinkedBlockingDeque插入总在最前,而remove()本身始终删除第一个元素,所以就变为了后进先出阻塞队列。
实际一般情况只重写offer(…)函数是不够的,但因为ThreadPoolExecutor默认只用到了BlockingQueueoffer(…)函数,所以这种简单重写后做为ThreadPoolExecutor的任务队列没问题。

LIFOLinkedBlockingDeque.java包下的LinkedBlockingDeque.javaBlockingDeque.javaDeque.java都是 Java 1.6 源码中的,这里不做分析。

4.2.65 DiskCacheUtils.java

磁盘缓存工具类,可用于查找或删除某个 uri 对应的磁盘缓存。

4.2.66 MemoryCacheUtils.java

内存缓存工具类。可用于根据 uri 生成内存缓存 key,缓存 key 比较,根据 uri 得到所有相关的 key 或图片,删除某个 uri 的内存缓存。
generateKey(String imageUri, ImageSize targetSize)
根据 uri 生成内存缓存 key,key 规则为[imageUri]_[width]x[height]

4.2.67 StorageUtils.java

得到图片 SD 卡缓存目录路径。
缓存目录优先选择/Android/data/[app_package_name]/cache;若无权限或不可用,则选择 App 在文件系统的缓存目录context.getCacheDir();若无权限或不可用,则选择/data/data/[app_package_name]/cache

如果缓存目录选择了/Android/data/[app_package_name]/cache,则新建.nomedia文件表示不允许类似 Galley 这些应用显示此文件夹下图片。不过在 4.0 系统有 Bug 这种方式不生效。

4.2.68 ImageSizeUtils.java

用于计算图片尺寸、缩放比例相关的工具类。

4.2.69 IoUtils.java

IO 相关工具类,包括 stream 拷贝,关闭等。

4.2.70 L.java

Log 工具类。

5. 杂谈

聊聊 LRU

UIL 的内存缓存默认使用了 LRU 算法。 LRU: Least Recently Used 近期最少使用算法, 选用了基于链表结构的 LinkedHashMap 作为存储结构。
假设情景:内存缓存设置的阈值只够存储两个 bitmap 对象,当 put 第三个 bitmap 对象时,将近期最少使用的 bitmap 对象移除。
图 1: 初始化 LinkedHashMap, 并按使用顺序来排序, accessOrder = true;
图 2: 向缓存池中放入 bitmap1 和 bitmap2 两个对象。
图 3: 继续放入第三个 bitmap3,根据假设情景,将会超过设定缓存池阈值。
图 4: 释放对 bitmap1 对象的引用。
图 5: bitmap1 对象被 GC 回收。




本文转载自:http://blog.csdn.net/oyangyujun/article/details/47419989

共有 人打赏支持
k
粉丝 1
博文 129
码字总数 0
作品 0
朝阳
私信 提问
Android 使用Universal Image Loader绘制带圆角的图片(一)

Android 使用Universal Image Loader绘制带圆角的图片(一) 绘制带圆角的控件难吗?貌似不难。对于一个普通layout或者widget,要绘制圆角,只要把 background设置成下面这样的drawable就行了...

yhchinabest
2015/07/20
0
0
github上的NB Android项目

最近想捣鼓捣鼓Android,下了Oschina的android client source,看得挺舒服的,写得很清晰,受益匪浅,想再深入了解下,当然是接着看优秀开源项目咯。google到CSDN上的“直接拿来用,最火的A...

pengzai
2013/05/13
0
0
安卓图片加载框架--Universal-Image-Loader

  今天来介绍图片加载的框架Android-Universal-Image-Loader   GITHUB上的下载路径为:https://github.com/nostra13/Android-Universal-Image-Loader   也可以自行百度下载。   首先...

痞子姜
2015/09/18
0
0
android 图片加载之边下载边显示的讨论。

最近,接触的项目的图片加载都有不少的应用。大概了解了,不外乎一下几种,或者兼顾几种做法: |-采用缓存来提高用户体验,也节约流量。 |-缓存上做文章,采用多种策略的缓存模式,来达到更加...

Justin_Chiang
2013/12/25
1K
7
最美应用-从Android研发工程师的角度之[最美时光]

最美应用-从Android研发工程师的角度之最美时光 @author ASCE1885的 Github 简书 微博 CSDN 最近发现最美应用这样一个网站,它会定期推介一些很有意思的app,作为开发者,每次看到很棒的app...

2tman
2015/08/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Netty 备录 (一)

入职新公司不久,修修补补1个月的bug,来了点实战性的技术---基于netty即时通信 还好之前对socket有所使用及了解,入手netty应该不是很难吧,好吧,的确有点难,刚看这玩意的时候,可能都不知道哪里...

_大侠__
昨天
4
0
Django简单介绍和用户访问流程

Python下有许多款不同的 Web 框架。Django是重量级选手中最有代表性的一位。许多成功的网站和APP都基于Django。 Django是一个开放源代码的Web应用框架,由Python写成。 Django遵守BSD版权,初...

枫叶云
昨天
8
0
EOS错误代码及中文释义

本文集汇总了EOS区块链常见错误代码及其含义,完整错误代码集请查看 EOS错误代码集 - 汇智网 EOS错误代码列表如下, <table class="table table-striped"> <thead> <tr><th>错误代码</th><t......

汇智网教程
昨天
4
0
Spring Cloud Stream消费失败后的处理策略(四):重新入队(RabbitMQ)

应用场景 之前我们已经通过《Spring Cloud Stream消费失败后的处理策略(一):自动重试》一文介绍了Spring Cloud Stream默认的消息重试功能。本文将介绍RabbitMQ的binder提供的另外一种重试...

程序猿DD
昨天
5
0
kiss原则

KISS 原则是用户体验的高层境界,简单地理解这句话,就是要把一个产品做得连白痴都会用,因而也被称为“懒人原则”。换句话说来,”简单就是美“。KISS 原则源于 David Mamet(大卫马梅)的电...

NB-One
昨天
14
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部