美文网首页
Android - Bitmap 的加载和 Cache

Android - Bitmap 的加载和 Cache

作者: DJN_ | 来源:发表于2017-08-18 14:18 被阅读0次

    Android - Bitmap 的加载和 Cache

    Android 应用可使用的最大堆内存受到 Android 系统的限制,通常情况为 16 M - 48 M,当应用使用的内存超出限制时就会引发内存溢出错误:java.lang.OutOfMemoryError

    大多数应用中都会涉及到 Bitmap ,而 Bitmap 往往会占用较多的内存,因此实现高效加载的同时控制好内存占用是加载 Bitmap 时需要遵循的原则。

    缓存在很多开发场景中都会涉及,实际开发中经常需要对 Bitmap 进行缓存,通过缓存 Bitmap,能够极大的提高图片加载效率以及用户体验,同时能够减轻内存负担。

    1 Bitmap 的加载

    Bitmap 指的是一张图片,可以是 png 格式也可以是 jpg 等其他常见的图片格式。
    android 中通常使用 BitmapFactory 来加载 Bitmap,BitmapFactory 提供了如下几种方法从不同位置加载 Bitmap:

    • BitmapFactory.decodeByteArray() 从字节数组中加载
    • BitmapFactory.decodeFile() 从文件中加载
    • BitmapFactory.decodeFileDescriptor() 通过”文件描述符“加载
    • BitmapFactory.decodeResource() 从资源中加载,通常为 drawable下的资源
    • BitmapFactory.decodeStream() 从输入流中加载
    1.1 如何高效加载 Bitmap?

    上面 BitmapFactory 的几个加载方法中每种加载方法都会有一个包含 BitmapFactory.Options 参数的重载方法,BitmapFactory.Options类可以将位图的尺寸按需进行压缩,只加载与 ImageView(或其它控件)尺寸相同的压缩尺寸后的位图,这样就可以将加载位图的大小压缩从而在一定程度上避免 OOM。

    使用BitmapFactory.Options加载位图时主要用到它的 inSampleSize 参数,即采样率,当 inSampleSize 为 1 时,采样后的图片大小与原始图片的大小相等;当 inSampleSize 大于 1 ,比如 2 时,采样后的图片宽高都为原始图片的 1/2 ,而像素为原始图片的 1/4,。

    比如有宽高为 1280 * 720 的一张图片,此时所占内存为 1280 * 720 * 4,当 inSampleSize 为 2 时,其正真加载尺寸为 640 * 360,所占内存为 640 * 360 * 4。

    官方文档指出,inSampleSize 的取值应该总是 2 的指数,当不是指数时将向下取整选择最接近的 2 的指数代替。

    调整方法如下:

    //调整bitmap 大小
        public static Bitmap bitmapResizeFromResource(Resources res, int id, int reqWidth, int reqHeight) {
            final BitmapFactory.Options options = new BitmapFactory.Options();
    
            //只解析图片的原始宽高,而不正真加载图片
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeResource(res, id, options);
    
            int inSampleSize = 1;
            if (reqWidth > 0 && reqHeight > 0) {
                final int height = options.outHeight;
                final int width = options.outWidth;
    
                //如果宽或高有任一者不满足要求就进行调整
                if (height > reqHeight || width > reqHeight) {
    
                    // inSampleSize 为 1 没有作用,使从 2 开始增加
                    final int halfHeight = height / 2;
                    final int halfWidth = width / 2;
    
                    while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth)
                        inSampleSize *= 2;
                }
            }
            options.inSampleSize = inSampleSize;
            options.inJustDecodeBounds = false;
    
            return BitmapFactory.decodeResource(res, id, options);
        }
    

    2 Android 中的缓存策略

    缓存策略在 Android 开发中有着广泛的使用场景,尤其在图片加载这个场景。

    当程序第一次从网络(或其它来源)加载图片后,就将其缓存到存储设备上,这样下次使用时就不需要再从网络上下载,很多时候为了提高用户体验,往往还会把图片在内存中再缓存一份,这样当应用打算从网络请求一张图片时,程序首先会从内存中去获取,如果内存中没有就去存储设备中获取,如果存储设备中也没有就从网络上下载。

    缓存策略并没有统一的标准,一般来说,缓存策略主要包含缓存的添加、获取和删除。缓存的添加和获取都比较好理解,那么为什么要删除呢?这是因为不管是内存缓存还是存储缓存,他们的大小都是有限制的(移动设备的内存可用容量和存储设备,如 SD 卡等的容量也是有限的),当缓存装满时,就需要通过缓存算法选择部分缓存数据删除,以添加新的缓存数据。

    目前常用的缓存算法是 LRU (Least Recently Used)算法,即最近最少使用算法。这个算法名字是不是很耳熟?《计算机操作系统》在 存储器管理 一章中介绍页面置换算法时就有 LRU 算法。

    2.1 LruCache

    LruCache 是 Android 提供的一个缓存类,LruCache 时一个泛型类,它内部采用一个 LinkedHashMap 以强引用的方式存储外界的缓存对象,其提供了 get 和 put 方法来完成缓存的获取和添加,当缓存满时,LruCache 会移除较早使用的缓存对象,然后再添加新的缓存对象。

    LruCache 支持删除操作,通过 remove 方法可以删除一个指定的缓存对象。

    2.2 弱引用、软引用、虚引用、强引用

    很早 java API 就添加了弱引用和软引用,引用类在垃圾回收工作的过程中有种要作用。

    2.2.1 强引用(Strong Reference)

    平常我们开发时创建对象的方式大都是以强引用的方式创建,如String str = "abc"中变量 str 就是字符串对象"abc" 的一个强引用。
    如下代码会移除 str 的强引用:str = null,此时垃圾回收器就会在某一时刻回收该对象。
    强引用可以阻止垃圾回收器回收对象。

    2.2.2 弱引用 (WeakReference)

    你可以通过如下方式创建一个软引用对象:
    String str = "test"
    WeakReference<String> wr = new WeakReference<String>(str);
    如果一个对象只有弱引用指向他,当移除强引用时(str = null),垃圾回收器会立即回收该对象。
    弱引用无法阻止垃圾回收器回收对象。

    2.2.3 软引用(SoftReference)

    String str = "test";
    SoftPreference sr = new SoftPreference(str)
    如果有软引用指向对象,当移除强引用时(str = null),对象不会立即被回收,只有在 JVM 需要内存时,才会回收该对象。
    软引无法阻止垃圾回收器回收对象,但可以延迟回收

    2.2.4 虚引用{PhantomReference}

    String str = "test"
    PhantomReference pr = new PhantomReference(str)
    当移除强引用时(str = null),拥有虚引用的对象可以在任何时刻被垃圾回收器回收。
    虚引用无法阻止垃圾回收器回收对象。

    参考文章:Java中弱引用、软引用、虚引用及强引用的区别

    2.3 DiskLruCache

    DiskLruCache 用于实现存储设备缓存,即磁盘缓存,它通过将缓存对象写入文件系统从而实现缓存。

    DiskLruCache 得到了 android 官方文档的推荐,但它不属于 Android SDK 的一部分,源码可在这里获得:DiskLruCache.java

    DiskLruCache 的使用不做介绍,可参看这篇文章:Android DiskLruCache完全解析,硬盘缓存的最佳方案


    相关文章

      网友评论

          本文标题:Android - Bitmap 的加载和 Cache

          本文链接:https://www.haomeiwen.com/subject/zhlarxtx.html