Android Universal Image Loader 学习笔记(三)

4.2.19 ProcessAndDisplayImageTask.java

处理并显示图片的Task,实现了Runnable接口。

主要函数:

(1) run()

主要通过 imageLoadingInfo 得到BitmapProcessor处理图片,并用处理后的图片和配置新建一个DisplayBitmapTaskImageAware中显示图片。

4.2.20 LoadAndDisplayImageTask.java

加载并显示图片的Task,实现了Runnable接口,用于从网络、文件系统或内存获取图片并解析,然后调用DisplayBitmapTaskImageAware中显示图片。

主要函数:

(1) run()

获取图片并显示,核心代码如下:

bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp == null || bmp.isRecycled()) {
    bmp = tryLoadBitmap();
    ...
    ...
    ...
    if (bmp != null && options.isCacheInMemory()) {
        L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey);
        configuration.memoryCache.put(memoryCacheKey, bmp);
    }
}
……
DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
runTask(displayBitmapTask, syncLoading, handler, engine);

从上面代码段中可以看到先是从内存缓存中去读取 bitmap 对象,若 bitmap 对象不存在,则调用 tryLoadBitmap() 函数获取 bitmap 对象,获取成功后若在 DisplayImageOptions.Builder 中设置了 cacheInMemory(true), 同时将 bitmap 对象缓存到内存中。
最后新建DisplayBitmapTask显示图片。

函数流程图如下:
Load and Display Image Task Flow Chart
1. 判断图片的内存缓存是否存在,若存在直接执行步骤 8;
2. 判断图片的磁盘缓存是否存在,若存在直接执行步骤 5;
3. 从网络上下载图片;
4. 将图片缓存在磁盘上;
5. 将图片 decode 成 bitmap 对象;
6. 根据DisplayImageOptions配置对图片进行预处理(Pre-process Bitmap);
7. 将 bitmap 对象缓存到内存中;
8. 根据DisplayImageOptions配置对图片进行后处理(Post-process Bitmap);
9. 执行DisplayBitmapTask将图片显示在相应的控件上。
流程图可以参见3. 流程图

(2) tryLoadBitmap()

从磁盘缓存或网络获取图片,核心代码如下:

File imageFile = configuration.diskCache.get(uri);
if (imageFile != null && imageFile.exists()) {
    ...
    bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));
}
if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
    ...
    String imageUriForDecoding = uri;
    if (options.isCacheOnDisk() && tryCacheImageOnDisk()) {
        imageFile = configuration.diskCache.get(uri);
        if (imageFile != null) {
            imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath());
        }
    }
    checkTaskNotActual();
    bitmap = decodeImage(imageUriForDecoding);
    ...
}

首先根据 uri 看看磁盘中是不是已经缓存了这个文件,如果已经缓存,调用 decodeImage 函数,将图片文件 decode 成 bitmap 对象;
如果 bitmap 不合法或缓存文件不存在,判断是否需要缓存在磁盘,需要则调用tryCacheImageOnDisk()函数去下载并缓存图片到本地磁盘,再通过decodeImage(imageUri)函数将图片文件 decode 成 bitmap 对象,否则直接通过decodeImage(imageUriForDecoding)下载图片并解析。

(3) tryCacheImageOnDisk()

下载图片并存储在磁盘内,根据磁盘缓存图片最长宽高的配置处理图片。

    loaded = downloadImage();

主要就是这一句话,调用下载器下载并保存图片。
如果你在ImageLoaderConfiguration中还配置了maxImageWidthForDiskCache或者maxImageHeightForDiskCache,还会调用resizeAndSaveImage()函数,调整图片尺寸,并保存新的图片文件。

(4) downloadImage()

下载图片并存储在磁盘内。调用getDownloader()得到ImageDownloader去下载图片。

(4) resizeAndSaveImage(int maxWidth, int maxHeight)

从磁盘缓存中得到图片,重新设置大小及进行一些处理后保存。

(5) getDownloader()

根据ImageLoaderEngine配置得到下载器。
如果不允许访问网络,则使用不允许访问网络的图片下载器NetworkDeniedImageDownloader;如果是慢网络情况,则使用慢网络情况下的图片下载器SlowNetworkImageDownloader;否则直接使用ImageLoaderConfiguration中的downloader

4.2.21 ImageLoadingInfo.java

加载和显示图片任务需要的信息。
String uri 图片 url。
String memoryCacheKey 图片缓存 key。
ImageAware imageAware 需要加载图片的对象。
ImageSize targetSize 图片的显示尺寸。
DisplayImageOptions options 图片显示的配置项。
ImageLoadingListener listener 图片加载各种时刻的回调接口。
ImageLoadingProgressListener progressListener 图片加载进度的回调接口。
ReentrantLock loadFromUriLock 图片加载中的重入锁。

4.2.22 ImageDownloader.java

图片下载接口。待实现函数

getStream(String imageUri, Object extra)

表示通过 uri 得到 InputStream。
通过内部定义的枚举Scheme, 可以看出 UIL 支持哪些图片来源。

HTTP("http"), HTTPS("https"), FILE("file"), CONTENT("content"), ASSETS("assets"), DRAWABLE("drawable"), UNKNOWN("");
4.2.23 BaseImageDownloader.java

ImageDownloader的具体实现类。得到上面各种Scheme对应的图片 InputStream。

主要函数

(1). getStream(String imageUri, Object extra)

getStream(…)函数内根据不同Scheme类型获取图片输入流。

@Override
public InputStream getStream(String imageUri, Object extra) throws IOException {
    switch (Scheme.ofUri(imageUri)) {
        case HTTP:
        case HTTPS:
            return getStreamFromNetwork(imageUri, extra);
        case FILE:
            return getStreamFromFile(imageUri, extra);
        case CONTENT:
            return getStreamFromContent(imageUri, extra);
        case ASSETS:
            return getStreamFromAssets(imageUri, extra);
        case DRAWABLE:
            return getStreamFromDrawable(imageUri, extra);
        case UNKNOWN:
        default:
            return getStreamFromOtherSource(imageUri, extra);
    }
}

具体见下面各函数介绍。

(2). getStreamFromNetwork(String imageUri, Object extra)

通过HttpURLConnection从网络获取图片的InputStream。支持 response code 为 3xx 的重定向。这里有个小细节代码如下:

try {
    imageStream = conn.getInputStream();
} catch (IOException e) {
    // Read all data to allow reuse connection (http://bit.ly/1ad35PY)
    IoUtils.readAndCloseStream(conn.getErrorStream());
    throw e;
}

在发生异常时会调用conn.getErrorStream()继续读取 Error Stream,这是为了利于网络连接回收及复用。但有意思的是在 Froyo(2.2) 之前,HttpURLConnection 有个重大 Bug,调用 close() 函数会影响连接池,导致连接复用失效,不少库通过在 2.3 之前使用 AndroidHttpClient 解决这个问题。

(3). getStreamFromFile(String imageUri, Object extra)

从文件系统获取图片的InputStream。如果 uri 是 video 类型,则需要单独得到 video 的缩略图返回,否则按照一般读取文件操作返回。

(4). getStreamFromContent(String imageUri, Object extra)

从 ContentProvider 获取图片的InputStream
如果是 video 类型,则先从MediaStore得到 video 的缩略图返回;
如果是联系人类型,通过ContactsContract.Contacts.openContactPhotoInputStream(res, uri)读取内容返回。
否则通过 ContentResolver.openInputStream(…) 读取内容返回。

(5). getStreamFromAssets(String imageUri, Object extra)

从 Assets 中获取图片的InputStream

(6). getStreamFromDrawable(String imageUri, Object extra)

从 Drawable 资源中获取图片的InputStream

(7). getStreamFromOtherSource(String imageUri, Object extra)

UNKNOWN(自定义)类型的处理,目前是直接抛出不支持的异常。

4.2.24 MemoryCache.java

Bitmap 内存缓存接口,需要实现的接口包括 get(…)、put(…)、remove(…)、clear()、keys()。

4.2.25 BaseMemoryCache.java

实现了MemoryCache主要函数的抽象类,以 Map\

protected abstract Reference<Bitmap> createReference(Bitmap value)

表示根据 Bitmap 创建一个 Reference 做为缓存对象。Reference 可以是 WeakReference、SoftReference 等。

4.2.26 WeakMemoryCache.java

WeakReference<Bitmap>做为缓存 value 的内存缓存,实现了BaseMemoryCache
实现了BaseMemoryCachecreateReference(Bitmap value)函数,直接返回一个new WeakReference<Bitmap>(value)做为缓存 value。

4.2.27 LimitedMemoryCache.java

限制总字节大小的内存缓存,继承自BaseMemoryCache的抽象类。
会在 put(…) 函数中判断总体大小是否超出了上限,是则循环删除缓存对象直到小于上限。删除顺序由抽象函数

protected abstract Bitmap removeNext()

决定。抽象函数

protected abstract int getSize(Bitmap value)

表示每个元素大小。

4.2.28 LargestLimitedMemoryCache.java

限制总字节大小的内存缓存,会在缓存满时优先删除 size 最大的元素,继承自LimitedMemoryCache
实现了LimitedMemoryCache缓存removeNext()函数,总是返回当前缓存中 size 最大的元素。

4.2.29 UsingFreqLimitedMemoryCache.java

限制总字节大小的内存缓存,会在缓存满时优先删除使用次数最少的元素,继承自LimitedMemoryCache
实现了LimitedMemoryCache缓存removeNext()函数,总是返回当前缓存中使用次数最少的元素。

4.2.30 LRULimitedMemoryCache.java

限制总字节大小的内存缓存,会在缓存满时优先删除最近最少使用的元素,继承自LimitedMemoryCache
通过new LinkedHashMap<String, Bitmap>(10, 1.1f, true)作为缓存池。LinkedHashMap 第三个参数表示是否需要根据访问顺序(accessOrder)排序,true 表示根据accessOrder排序,最近访问的跟最新加入的一样放到最后面,false 表示根据插入顺序排序。这里为 true 且缓存满时始终删除第一个元素,即始终删除最近最少访问的元素。
实现了LimitedMemoryCache缓存removeNext()函数,总是返回第一个元素,即最近最少使用的元素。

4.2.31 FIFOLimitedMemoryCache.java

限制总字节大小的内存缓存,会在缓存满时优先删除先进入缓存的元素,继承自LimitedMemoryCache
实现了LimitedMemoryCache缓存removeNext()函数,总是返回最先进入缓存的元素。

以上所有LimitedMemoryCache子类都有个问题,就是 Bitmap 虽然通过WeakReference<Bitmap>包装,但实际根本不会被虚拟机回收,因为他们子类中同时都保留了 Bitmap 的强引用。大都是 UIL 早期实现的版本,不推荐使用。

4.2.32 LruMemoryCache.java

限制总字节大小的内存缓存,会在缓存满时优先删除最近最少使用的元素,实现了MemoryCache。LRU(Least Recently Used) 为最近最少使用算法。

new LinkedHashMap<String, Bitmap>(0, 0.75f, true)作为缓存池。LinkedHashMap 第三个参数表示是否需要根据访问顺序(accessOrder)排序,true 表示根据accessOrder排序,最近访问的跟最新加入的一样放到最后面,false 表示根据插入顺序排序。这里为 true 且缓存满时始终删除第一个元素,即始终删除最近最少访问的元素。

put(…)函数中通过trimToSize(int maxSize)函数判断总体大小是否超出了上限,是则删除第缓存池中第一个元素,即最近最少使用的元素,直到总体大小小于上限。

LruMemoryCache功能上与LRULimitedMemoryCache类似,不过在实现上更加优雅。用简单的实现接口方式,而不是不断继承的方式。

4.2.33 LimitedAgeMemoryCache.java

限制了对象最长存活周期的内存缓存。
MemoryCache的装饰者,相当于为MemoryCache添加了一个特性。以一个MemoryCache内存缓存和一个 maxAge 做为构造函数入参。在 get(…) 时判断如果对象存活时间已经超过设置的最长时间,则删除。

4.2.34 FuzzyKeyMemoryCache.java

可以将某些原本不同的 key 看做相等,在 put 时删除这些相等的 key。
MemoryCache的装饰者,相当于为MemoryCache添加了一个特性。以一个MemoryCache内存缓存和一个 keyComparator 做为构造函数入参。在 put(…) 时判断如果 key 与缓存中已有 key 经过Comparator比较后相等,则删除之前的元素。

4.2.35 FileNameGenerator.java

根据 uri 得到文件名的接口。

4.2.36 HashCodeFileNameGenerator.java

以 uri 的 hashCode 作为文件名。

4.2.37 Md5FileNameGenerator.java

以 uri 的 MD5 值作为文件名。

4.2.38 DiskCache.java

图片的磁盘缓存接口。

主要函数:

(1) File get(String imageUri)

根据原始图片的 uri 去获取缓存图片的文件。

(2) boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener)

保存 imageStream 到磁盘中,listener 表示保存进度且可在其中取消某些段的保存。

(3) boolean save(String imageUri, Bitmap bitmap)

保存图片到磁盘。

(4) boolean remove(String imageUri)

根据图片 uri 删除缓存图片。

(5) void close()

关闭磁盘缓存,并释放资源。

(6) void clear()

清空磁盘缓存。

(7) File getDirectory()

得到磁盘缓存的根目录。

4.2.39 BaseDiskCache.java

一个无大小限制的本地图片缓存,实现了DiskCache主要函数的抽象类。
图片缓存在cacheDir文件夹内,当cacheDir不可用时,则使用备库reserveCacheDir

主要函数:

(1). save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener)

先根据imageUri得到目标文件,将imageStream先写入与目标文件同一文件夹的 .tmp 结尾的临时文件内,若未被listener取消且写入成功则将临时文件重命名为目标文件并返回 true,否则删除临时文件并返回 false。

(2). save(String imageUri, Bitmap bitmap)

先根据imageUri得到目标文件,通过Bitmap.compress(…)函数将bitmap先写入与目标文件同一文件夹的 .tmp 结尾的临时文件内,若写入成功则将临时文件重命名为目标文件并返回 true,否则删除临时文件并返回 false。

(3). File getFile(String imageUri)

根据 imageUri 和 fileNameGenerator得到文件名,返回cacheDir内该文件,若cacheDir不可用,则使用备库reserveCacheDir

4.2.40 LimitedAgeDiskCache.java

限制了缓存对象最长存活周期的磁盘缓存,继承自BaseDiskCache
在 get(…) 时判断如果缓存对象存活时间已经超过设置的最长时间,则删除。在 save(…) 时保存当存时间作为对象的创建时间。

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(…) 中
1. 如果viewScaleType等于ViewScaleType.FIT_INSIDE
1.1 如果scaleType等于ImageScaleType.IN_SAMPLE_POWER_OF_2,则缩放比例从 1 开始不断 *2 直到宽或高小于最大尺寸;
1.2 否则取宽和高分别与最大尺寸比例中较大值,即Math.max(srcWidth / targetWidth, srcHeight / targetHeight)
2. 如果scaleType等于ViewScaleType.CROP
2.1 如果scaleType等于ImageScaleType.IN_SAMPLE_POWER_OF_2,则缩放比例从 1 开始不断 *2 直到宽和高都小于最大尺寸。
2.2 否则取宽和高分别与最大尺寸比例中较小值,即Math.min(srcWidth / targetWidth, srcHeight / targetHeight)
3. 最后判断宽和高是否超过最大值,如果是 *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 回收。




©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页