Android高效加载图片和缓存策略LRU,DiskLRU

1.高效加载图片

加载图片主要用到BitmapFactory类的四个方法:decodeFile()从文件中加载,decodeResource()从资源文件中加载,decodeStream()从输入流,decodeByteArray()从自己数组中加载,
decodefile和decodeResource调用decodeStream实现,四个方法底层都是有native方法实现的。

高效加载图片思想:根据imageView的大小对图片进行压缩,减少内存使用。

压缩图片使用BitmapFactory.Options类的inSampleSize参数(采样率),该参数指将图片的宽高的像素各除SampleSize,inSampleSize总是2的倍数,系统会自行处理,小1取1。
获取到了采样率之后就可以使用BitmapFactory进行加载了。
加载流程:

1.BitmapFactory.Options的inJustDecodeBounds设为true,只对图片的宽高信息进行解析,并将宽高保存在options内,不会对图片主体进行解析。获取的宽高信息与图片所在目录和手机屏幕的密度相关,如果手机屏幕密度大于图片所在Drawable文件夹对应的屏幕密度,那么图片会被放大,反之会被缩小。

2.使用获得的宽高信息和需要的View的宽高,计算得到采样率。

3.再次使用BitmapFactory加载图片,但是将Options.inJustDecodeBounds 设为false,options使用前面得到的,就可以将图片进行压缩了。

以上步骤对于decodeStream来说可能存在问题,因为decodeStream两次操作会改变文件流的位置属性,导致第二次decodeStream时得到的是null,可以通过文件流的文件描述符来加载图片,BitMapFactory.decodeFileDescriptor(),lrudiskcache就是使用的该方式从文件流中加载。

LRU缓存策略

缓存策略主要指,缓存的添加,删除和获取的三个方法对应的算法,即如何将旧的删除新的添加的策略。

LRU,最近最少使用策略,思想:当缓存满时将最近最少使用的对象移除。

LRUCache是个泛型类,内部使用LinkedHashMap以强引用的方式缓存对象。

强引用:平时常用的引用方式。

软引用:当对象只有软引用存在时,系统在内存不足时会将该对象回收掉。

弱引用:当对象只有弱引用时,系统只要gc时就会将该对象回收。

来看看lrucache的源码。

private final LinkedHashMap<K, V> map;

    /** Size of this cache in units. Not necessarily the number of elements. */
    private int size;
    private int maxSize;

    private int putCount;
    private int createCount;
    private int evictionCount;
    private int hitCount;
    private int missCount;

    /**
     * @param maxSize for caches that do not override {@link #sizeOf}, this is
     *     the maximum number of entries in the cache. For all other caches,
     *     this is the maximum sum of the sizes of the entries in this cache.
     */
    public LruCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
    }

成员包含了一些统计数据,构造函数创建了一个hashmap指定加载因子为0.75

看看get方法

/**
     * Returns the value for {@code key} if it exists in the cache or can be
     * created by {@code #create}. If a value was returned, it is moved to the
     * head of the queue. This returns null if a value is not cached and cannot
     * be created.
     */
    public final V get(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V mapValue;
        synchronized (this) {
            mapValue = map.get(key);
            if (mapValue != null) {
                hitCount++;
                return mapValue;
            }
            missCount++;
        }

        /*
         * Attempt to create a value. This may take a long time, and the map
         * may be different when create() returns. If a conflicting value was
         * added to the map while create() was working, we leave that value in
         * the map and release the created value.
         */

        V createdValue = create(key);
        if (createdValue == null) {
            return null;
        }

        synchronized (this) {
            createCount++;
            mapValue = map.put(key, createdValue);

            if (mapValue != null) {
                // There was a conflict so undo that last put
                map.put(key, mapValue);
            } else {
                size += safeSizeOf(key, createdValue);
            }
        }

        if (mapValue != null) {
            entryRemoved(false, key, createdValue, mapValue);
            return mapValue;
        } else {
            trimToSize(maxSize);
            return createdValue;
        }
    }

看到lrucache是线程同步的,同理put方法也是

/**
     * Caches {@code value} for {@code key}. The value is moved to the head of
     * the queue.
     *
     * @return the previous value mapped by {@code key}.
     */
    public final V put(K key, V value) {
        if (key == null || value == null) {
            throw new NullPointerException("key == null || value == null");
        }

        V previous;
        synchronized (this) {
            putCount++;
            size += safeSizeOf(key, value);
            previous = map.put(key, value);
            if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            entryRemoved(false, key, previous, value);
        }

        trimToSize(maxSize);
        return previous;
    }

使用时,我们在创建lrucache对象时,需要复写其sizeof方法,写出要缓存的对象的内存计算方法将结果返回,列如缓存图片可以这样写:

int sizeof(String key,Bitmap bitmap){
    return bitmap.getRowBytes()*bitmap.getHeight()/1024;
}

DiskLRUCache

将缓存对象写入文件系统实现缓存效果。

disklru的使用参考

网络图片加载的思路:

加载图片步骤:内存缓存,disk缓存,网络下载

本地没有图片的加载图片步骤:下载图片httpurlconnection获得Stream 存入disklrucache,对图片进行高效加载(即压缩),同时加入到内存lrucache,显示到imageview上。

同步和异步加载

同步加载是指由调用者自行创建线程实现不在ui线程中加载图片,可以在内部判断不能再主线程执行

异步加载使用线程池和handler(使用mainlooper),内存缓存没有情况下,将加载图片操作在子线程执行,使用runnabe创建对象,用线程池excutor执行。也可以使用asynctask,但不能实现多线程并发(在3.0以后asyncTask为单线程的线程池)。需要解决图片错位的问题,给imageview设置tag,当tag和url不同时,不给imageview设置图片。

优化列表滑动卡顿:滑动时不加载图片减少线程创建,getView中不进行耗时操作,开启硬件加速

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值