Android第三方资源使用之ImageCache

转载请注明出处:王亟亟的大牛之路

引用库的原作者Git:https://github.com/Trinea

现在很多需要动态呈现的View都使用到了H5和WebView,而有些使用的还是传统的异步加载操作,今天写的是传统的View的实现(H5的可以看这篇文章:http://blog.csdn.net/ddwhan0123/article/details/49683799)

我们常用的诸如ImageLoader Picasso 都有类似的效果,今天上的是国内大牛Trinea的ImageCache,那为什么用他的这个呢?

个人觉得他更轻量级,效果简单明了,一些衍生和伸展性做的也还不错。

一.说之前,还是说一下一些比较重要的知识点。(我们不生产知识,我们只是知识的搬运工)

缓存:java的对象创建需要分配资源较耗费时间,加上创建的对象越多会造成越频繁的gc影响系统响应。主要使用单例模式、缓存(图片缓存、线程池、View缓存、IO缓存、消息缓存、通知栏notification缓存)及其他方式减少对象创建。

然后缓存中,又图片缓存、线程池、View缓存、IO缓存、消息缓存、通知栏notification缓存等。(这不是我们主要讲的内容,不知道可以自己Google,这里只是例举下)

那么,在实用场景下会有哪些常见的异步加载问题呢?

a. 行item图片显示重复
这个显示重复是指当前行item显示了之前某行item的图片。
比如ListView滑动到第2行会异步加载某个图片,但是加载很慢,加载过程中listView已经滑动到了第14行,且滑动过程中该图片加载结束,第2行已不在屏幕内,根据上面介绍的缓存原理,第2行的view可能被第14行复用,这样我们看到的就是第14行显示了本该属于第2行的图片,造成显示重复。

b. 行item图片显示错乱
这个显示错乱是指某行item显示了不属于该行item的图片。
比如ListView滑动到第2行会异步加载某个图片,但是加载很慢,加载过程中listView已经滑动到了第14行,第2行已不在屏幕内,根据上面介绍的缓存原理,第2行的view可能被第14行复用,第14行显示了第2行的View,这时之前的图片加载结束,就会显示在第14行,造成错乱。

c. 行item图片显示闪烁
上面b的情况,第14行图片又很快加载结束,所以我们看到第14行先显示了第2行的图片,立马又显示了自己的图片进行覆盖造成闪烁错乱

因为,如果我们用 Back键回到上一个Activity再进来的时候是不是还要重新加载?或者我第一次加载存到本地,第二次再读本地IO?所以,缓存成为提高用户体验和节约性能一个很好的途径。

二.ImageCache

ImageCache有什么优势呢?

(1). 使用简单 (2). 轻松获取及预取新图片 (3). 包含二级缓存 (4). 可选择多种缓存算法(FIFO、LIFO、LRU、MRU、LFU、MFU等13种)或自定义缓存算法 (5). 可方便的保存及初始化恢复数据 (6). 支持文件sd卡保存及自定义文件名规则 (7). 省流量性能佳(有且仅有一个线程获取图片) (8). 支持不同类型网络处理 (9). 可根据系统配置初始化缓存 (10). 扩展性强 (11). 支持等待队列 (12). 包含map的大多数接口。

-1.使用:

a.初始化一个ImageCache 对象

public static final ImageCache IMAGE_CACHE = CacheManager.getImageCache();

b.需要加载图片的地方调用get(String imageUrl, View view)异步加载图片

IMAGE_CACHE.get(imageUrl, imageView);

就是辣么简单,效果就能达到。

当然,对缓存,线程等等等一系列有要求的,也可以加以设置:

/** image cache **/
public static final ImageCache IMAGE_CACHE = new ImageCache();

static {
    OnImageCallbackListener imageCallBack = new OnImageCallbackListener() {

        private static final long serialVersionUID = 1L;

        // callback function before get image, run on ui thread
        @Override
        public void onPreGet(String imageUrl, View view) {
            // Log.e(TAG_CACHE, "pre get image");
        }

        // callback function after get image successfully, run on ui thread
        @Override
        public void onGetSuccess(String imageUrl, Bitmap loadedImage, View view, boolean isInCache) {
            // can be another view child, like textView and so on
            if (view != null && loadedImage != null && view instanceof ImageView) {
                ImageView imageView = (ImageView)view;
                imageView.setImageBitmap(loadedImage);
            }
        }

        // callback function after get image failed, run on ui thread
        @Override
        public void onGetFailed(String imageUrl, Bitmap loadedImage, View view, FailedReason failedReason) {
            Log.e(TAG_CACHE, new StringBuilder(128).append("get image ").append(imageUrl).append(" error")
                                                   .toString());
        }

        @Override
        public void onGetNotInCache(String imageUrl, View view) {

        }
    };
    IMAGE_CACHE.setOnImageCallbackListener(imageCallBack);
}

-2.功能

(1) 多种构造函数,可根据系统配置初始化缓存
public ImageCache()
public ImageCache(int primaryCacheMaxSize)
public ImageCache(int primaryCacheMaxSize, int secondaryCacheMaxSize)
public ImageCache(int primaryCacheMaxSize, int primaryCacheThreadPoolSize, int secondaryCacheMaxSize, int secondaryCacheThreadPoolSize)
支持四种构造函数,支持一级和二级缓存大小及获取图片线程池大小的设置。默认会根据系统可用内存大小设置缓存大小,根据系统Cpu个数设置线程池大小。

(2)、获取图片及自动预取
get(String imageUrl, View view)异步获取图片,在图片获取成功后自动调用OnImageCallbackListener的onGetSuccess函数,返回是否已在缓存中
get(String imageUrl, List urlList, View view)异步获取图片,在图片获取成功后自动调用OnImageCallbackListener的onGetSuccess函数,并且根据imageUrl在urlList中的位置向前向后预取图片,返回是否已在缓存中。

public void setForwardCacheNumber(int forwardCacheNumber) 向前预取图片个数设置,默认为PreloadDataCache#DEFAULT_FORWARD_CACHE_NUMBER
public void setBackwardCacheNumber(int backwardCacheNumber)向后预取图片个数设置默认,默认为PreloadDataCache#DEFAULT_BACKWARD_CACHE_NUMBER

public CacheObject get(K key)
public CacheObject get(K key, List keyList)
两个接口是直接同步获取图片,且获取成功后不会调用OnImageCallbackListener的onGetSuccess函数

(3)、设置缓存算法
setCacheFullRemoveType(CacheFullRemoveType cacheFullRemoveType)
设置缓存算法,缓存算法即为缓存满时为了插入新数据,删除旧数据的规则。

目前包括FIFO、LIFO、LRU、MRU、LFU、MFU、优先级低先删除、优先级高先删除、数据小先删除、数据大先删除、图片小先删除、图片大先删除、永不删除。还可以通过实现CacheFullRemoveType来自定义缓存算法。。默认为RemoveTypeUsedCountSmall,即LRU使用频率低先删除。下面详细介绍各个算法:
RemoveTypeEnterTimeFirst FIFO先进先出,先进入先删除
RemoveTypeEnterTimeLast LIFO后进先出,后进入先删除
RemoveTypeLastUsedTimeFirst LRU(Least Recently User),最先使用先删除
RemoveTypeLastUsedTimeLast MRU(Most Recently Used),最近使用先删除
RemoveTypeUsedCountSmall LFU(Least Frequently Used),使用频率低先删除
RemoveTypeUsedCountBig MRU(Most Frequently Used),使用频率高先删除
RemoveTypePriorityLow 优先级低先删除
RemoveTypePriorityHigh 优先级低先删除
RemoveTypeBitmapSmall 图片小的先删除
RemoveTypeBitmapLarge 图片大的先删除
RemoveTypeDataBig 数据大先删除,根据缓存数据的compareTo函数决定
RemoveTypeDataSmall 数据小先删除,根据缓存数据的compareTo函数决定
RemoveTypeNotRemove 不删除,缓存满时不再允许插入新数据

自定义缓存算法只需要实现CacheFullRemoveType的compare方法即可。比较结果小于0表示会被先删除

public class RemoveTypePriorityHigh<T> implements CacheFullRemoveType<T> {

    private static final long serialVersionUID = 1L;

    @Override
    public int compare(CacheObject<T> obj1, CacheObject<T> obj2) {
        return (obj2.getPriority() > obj1.getPriority()) ? 1 : ((obj2.getPriority() == obj1.getPriority()) ? 0 : -1);
    }
}

(4)、保存及初始化恢复数据
public boolean saveDataToDb(Context context, String tag)
保存数据到数据库,可在程序退出时调用,不建议在每个activity onDestrory时调用,而是整个程序退出(比如”退出确认对话框”点击确认)时,见本文3.1常见问题解答。
public void initData(Context context, String tag)
初始化恢复数据,可在程序刚开始加载时调用,不建议在每个activity oncreate调用,而是整个程序初始化(比如Application的onCreate函数)时,见本文3.1常见问题解答。

(5)、是否启用队列
setOpenWaitingQueue(boolean isOpenWaitingQueue)
当不同view通过get函数获取图片时,是否开启等待队列。
若开启,保存所有view,图片获取成功后依次调用OnImageCallbackListener的onGetSuccess函数;否则仅保存最后调用get的view,图片获取成功后调用OnImageCallbackListener的onGetSuccess函数
默认开启队列等待。如果希望最优性能且场景满足,可设置为false。

(6)、设置图片获取方式接口
setOnGetDataListener(OnGetDataListener

setOnGetDataListener(ImageCacheManager.getImageFromSdcardListener());

注意这种方式compressListener是无效的,如果希望利用compressListener,可以如下设置:

setOnGetDataListener(new OnGetDataListener<String, Bitmap>() {

    private static final long serialVersionUID = 1L;

    @Override
    public CacheObject<Bitmap> onGetData(String imagePath) {
        if (!FileUtils.isFileExist(imagePath)) {
            return null;
        }

        CompressListener compressListener = IMAGE_CACHE.getCompressListener();
        int compressSize = 0;
        if (compressListener != null) {
            compressSize = compressListener.getCompressSize(imagePath);
        }
        Bitmap bm;
        if (compressSize > 1) {
            BitmapFactory.Options option = new BitmapFactory.Options();
            option.inSampleSize = compressSize;
            bm = BitmapFactory.decodeFile(imagePath, option);
        } else {
            bm = BitmapFactory.decodeFile(imagePath);
        }
        return (bm == null ? null : new CacheObject<Bitmap>(bm));
    }
});

5、内存溢出OOM问题?
通过setCompressListener接口压缩图片,getCompressSize返回值为图片长宽缩放比例,同BitmapFactory.Options#inSampleSize


IMAGE_CACHE.setCompressListener(new CompressListener() {

    @Override
    public int getCompressSize(String imagePath) {
        if (FileUtils.isFileExist(imagePath)) {
            long fileSize = FileUtils.getFileSize(imagePath) / 1000;
            /** * if image bigger than 100k, compress to 1/(n + 1) width and 1/(n + 1) height, n is fileSize / 100k **/
            if (fileSize > 100) {
                return (int)(fileSize / 100) + 1;
            }
        }
        return 1;
    }
});

还可见 图片OutOfMemory异常bitmap size exceeds VM budget的原因及解决方法

更多内容可以在作者的文档中找到。

接下来贴一下我们的示例代码:

包目录:
Android第三方资源使用之ImageCache_第1张图片

源码地址:https://github.com/ddwhan0123/BlogSample/tree/master/ImageCacheDemo

这里写图片描述

感谢观众老爷点个赞,谢谢

你可能感兴趣的:(android,git,异步,库)