Bitmap 的加载和 Cache

Android 中如何高效地加载 Bitmap 是一个很重要也很容易被我们忽视的问题。

Bitmap 的高效加载

  1. BitmapFactory 类提供了:decodeFiledecodeResourcedecodeStreamdecodeByteArray 以及 decodeFileDescriptor 等几类方法来加载一个 Bitmap 对象。
  2. 高效加载 Bitmap 的核心思想就是设置 BitmapFactory.OptionsinSampleSize 采样率属性来加载所需尺寸的图片。

    inSampleSize = 1 时加载原图,inSampleSize = 2 时加载的像素为原图的 1/4,以此类推。官方推荐设置 inSampleSize 的值为 2 的指数。

  3. 获取采样率的流程:
    1. BitmapFactory.OptionsinJustDecodeBounds 参数设为 true 并加载图片。
    2. BitmapFactory.Options 中取出图片的原始宽高信息,它们对应于 outWidthoutHeight
    3. 根据采样率的规则并结合目标 View 的所需大小计算出采样率 inSampleSize
    4. BitmapFactory.OptionsinJustDecodeBounds 参数设为 false 然后重新加载图片。
      示例代码
      1234567891011121314151617181920212223242526272829303132333435363738394041
       public Bitmap (Resources res,        int resId, int reqWidth, int reqHeight) {
      
          // 并不会真正地去加载图片    final BitmapFactory.Options options = new BitmapFactory.Options();    options.inJustDecodeBounds = true;    BitmapFactory.decodeResource(res, resId, options);
      
          // 计算采样率    options.inSampleSize = calculateInSampleSize(options, reqWidth,            reqHeight);
      
          // 重新加载图片    options.inJustDecodeBounds = false;    return BitmapFactory.decodeResource(res, resId, options);}
      
      public int calculateInSampleSize(BitmapFactory.Options options,        int reqWidth, int reqHeight) {    if (reqWidth == 0 || reqHeight == 0) {        return 1;    }
      
          // 图片的原始宽高信息    final int height = options.outHeight;    final int width = options.outWidth;
      
          int inSampleSize = 1;
      
          if (height > reqHeight || width > reqWidth) {        final int halfHeight = height / 2;        final int halfWidth = width / 2;
      
              while ((halfHeight / inSampleSize) >= reqHeight                && (halfWidth / inSampleSize) >= reqWidth) {            inSampleSize *= 2;        }    }
      
          return inSampleSize;}

Android 中的缓存策略

  1. 缓存策略并没有统一的标准,一般来说缓存策略主要包含缓存的添加、获取和删除这三类操作。
  2. 常用的缓存算法是 LRU(Least Recently Used),翻译为:近期最少使用算法。它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。
  3. Android 中常见的 LRU 算法缓存有两种:LruCache(内存缓存) 和 DiskLruCache(存储设备缓存),一般情况下会将两者结合使用。
LruCache
  1. LruCache 是一个泛型类,内部采用一个 LinkedHashMap<K, V> 以强引用的方式存储外界的缓存对象,提供了 getput 等操作方法,当存储满时,会移除较早使用的缓存对象,再添加新的缓存对象。此外,LruCache 是线程安全的。

    三种引用的区别:

    • 强引用:直接的对象引用
    • 软引用:当一个对象只有软引用存在时,系统内存不足时此对象会被 gc 回收
    • 弱引用:当一个对象只有弱引用存在时,此对象会随时被 gc 回收
  2. LruCache 典型示例代码:
    12345678910111213141516
    // 获取当前可用的最大内存int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); // KB// 设置缓存大小int cacheSize = maxMemory / 8;
    
    LruCache<String, Bitmap> lruCache = new LruCache<String, Bitmap>(cacheSize) {
    
        protected int sizeOf(String key, Bitmap value) {        // 重写此方法计算缓存对象的大小        return value.getRowBytes() * value.getHeight() / 1024;    }};// 添加缓存lruCache.put("liyu", bitmap);// 获取缓存Bitmap bitmap = lruCache.get("liyu");
DiskLruCache

DiskLruCache 用于实现存储设备缓存,即磁盘缓存,它通过将缓存对象写入文件系统从而实现缓存的效果。项目地址:https://github.com/JakeWharton/DiskLruCache

  1. DiskLruCache 的创建

    1234567
     /*** @param directory 缓存路径* @param appVersion 一般为 1,当版本号变更时,会清空缓存文件* @param valueCount 单个节点所对应的数据个数,一般为 1* @param maxSize 缓存总大小,超过的话会清除一些缓存*/ public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
  2. DiskLruCache 的缓存添加
    1234567891011
    String key = hashKeyFormUrl(url); // url 转换下防止特殊字符影响DiskLruCache.Editor editor = mDiskLruCache.edit(key);if (editor != null) {    OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX); // DISK_CACHE_INDEX 为 0,因为设置的一个节点只有一个数据    if (downloadUrlToStream(url, outputStream)) { //下载        editor.commit(); // 下载成功提交缓存    } else {        editor.abort(); // 出现异常则取消缓存    }    mDiskLruCache.flush(); // 强制缓冲文件保存到文件系统}
  3. DiskLruCache 的缓存查找
    123456789101112
    Bitmap bitmap = null;String key = hashKeyFormUrl(url); // url 转换下防止特殊字符影响DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key); // 获取缓存快照if (snapShot != null) {    FileInputStream fileInputStream = (FileInputStream)snapShot.getInputStream(DISK_CACHE_INDEX); // DISK_CACHE_INDEX 为 0,因为设置的一个节点只有一个数据    FileDescriptor fileDescriptor = fileInputStream.getFD(); // 解决 decodeStream 缩放 bitmap 第二次为 null 的问题    bitmap = mImageResizer.decodeSampledBitmapFromFileDescriptor(fileDescriptor,            reqWidth, reqHeight); // 缩放图片    if (bitmap != null) {        addBitmapToMemoryCache(key, bitmap); // 添加到内存缓存,方便下次快速获取    }}
ImageLoader 的实现

一个优秀的图片加载器应具备:

  • 图片的同步加载
  • 图片的异步加载
  • 图片压缩
  • 内存缓存
  • 磁盘缓存
  • 网络拉取

完整的 ImageLoader 示例可以参考源码

ImageLoader 使用

  1. 非 WiFi 环境下使用时应有提示,避免消耗过多流量。
  2. 列表视图例如 ListView,不要在 getView 中执行耗时操作。
  3. 控制异步任务的执行频率,例如列表滚动的时候不加载图片,等完全停止时才开始加载。
  4. 如果仍有卡顿现象,可以尝试开启硬件加速。

原文:大专栏  Bitmap 的加载和 Cache

原文地址:https://www.cnblogs.com/petewell/p/11597445.html

时间: 2024-11-13 08:17:30

Bitmap 的加载和 Cache的相关文章

Android 基础 十二 Bitmap的加载和Cache

本章的主题是Bitmap的加载和Cache,主要包含三个方面的内容.首先讲述如何有效地加载一个Bitmap,这是一个很有意义的话题,由于Bitmap的特殊性以及Android对单个应用所施加的内存限制,比如16MB,这导致Bitmap加载的时候很容易出现内存溢出.下面这个异常信息在开发中应该经常遇到: 因此如何高效的加载Bitmap是一个很重要也很容易被开发者或忽视的问题. 接着介绍Android中常用的缓存策略,缓存策略是一种通用的思想,可以用在很多场景中,但是实际开发中经常需要用Bitmap

Bitmap的加载与Cache(一)

如何有效的加载一个bitmap,由于Bitmap的特殊性以及Android对单个应用所施加的内存限制,比如16MB,这就导致加载Bitmap的时候很容易出现内存溢出. 因此,如何高效的加载bitmap是一个很重要也很容易被开发者忽略的问题. Bitmap的高效加载: 如何加载一张图片呢?BitmapFactory类提供了四类方法:decodedFile,decodedResource,decodedStream和decodedByteArray,分别用于支持文件系统,资源,输入流以及字节数组中加

Bitmap的加载和Cache

前提:由于Bitmap的特殊性以及Android对单个应用所施加的内存限制,导致加载Bitmap的时候很容易出现内存溢出. 下面这个一场信息在开发中应该市场遇到: java.lang.OutofMemoryError: bitmap size exceeds VM budget Android实际开发中经常需要用Bitmap做缓存,通过换粗策略,我们不需要每次都从网络上请求图片或者从存储设备中加载图片,这样就极大地提高了图片的加载效率以及产品的用户体验. 目前比较常用的缓存策略是LruCache

第12章 Bitmap的加载和Cache

高效加载 BitmapFactory类提供四种方法: decodeFile:从文件,间接调用decodeStream decodeResource:从资源,间接调用decodeStream decodeStream:输入流 decodeByteArray:字节数组中 使用BitmapFactory.options按一定采样率来加载缩小后图片来避免OOM. public class ImageResizer { private static final String TAG = "ImageRes

Android艺术——Bitmap高效加载和缓存代码分析(2)

Bitmap的加载与缓存代码分析: 图片的压缩 比如有一张1024*768像素的图像要被载入内存,然而最终你要用到的图片大小其实只有128*96,那么我们会浪费很大一部分内存,这显然是没有必要的,下面是一个实例: public static int calculateInSampleSize(              BitmapFactory.Options options, int reqWidth, int reqHeight) {      // Raw height and widt

Android艺术——Bitmap高效加载和缓存(1)

通过Bitmap我们可以设计一个ImageLoader,实现应该具有的功能是: 图片的同步加载:图片的异步加载:图片的压缩:内存缓存:磁盘缓存:网络获取: 1.加载 首先提到加载:BitmapFactory类提供了四类方法:decodeFile.decodeResource.decodeStream和decideByteArray.分别是文件系统.资源.输入流和字节数加载Bitmap对象. 2.压缩 如何进行图片的压缩?首先我们为什么图片压缩呢?因为很多时候ImageView尺寸小于图片原始尺寸

使用iFrame动态加载Application Cache

为了避免缓存主页面,一般使用动态iFrame的方式来加载Application Cache,方法如下: 1 updateImageCache: function () { 2 3 if (null == $("iframe")) { 4 5 } else { 6 $("iframe").remove(); 7 } 8 9 var iframe = document.createElement('iframe'); 10 iframe.setAttribute('wi

关于Bitmap的加载(一)

Android中的Bitmap的加载,为了更好的用户体验,有非常多的奇巧淫技.要达到的目的是,一边加载一边看,不卡屏.图片根据布局大小显示.如果图片大了,要合理的缩小,防止内存溢出. 于是,我把Android training的讲解记录下来. 首先,是单一图片的加载. 单一图片,加载最首要的问题是判断图片的大小.如果图片过大,我们需要对图片进行一定比例的压缩. 先通过图片本身的宽高和要求的宽高,计算图片的缩放比例. public static int calculateInSampleSize(

关于Bitmap的加载(二)

接上节,上节记录了Bitmap的加载,用上节的方法,我们可以方便地压缩图片,进行加载. 然而,有时候,当出来大量的图片或者是一张大图要加载时,这一过程所花的时间就会显得很长.甚至长到用户可以感觉出来,即UI的卡顿.因此,我们需要一个方法让图片自己加载去吧,不能干扰用户的操作.所以,我们需要用一个新的线程来完成图片加载的操作.其中最简单便捷的方法,就是Use an AsyncTask. 首先创建一个AsyncTask的子类: class BitmapWorkerTask extends Async