美文网首页
第12章 Bitmap的加载和Cache

第12章 Bitmap的加载和Cache

作者: Xerrard | 来源:发表于2017-06-23 08:57 被阅读25次

    12.1 Bitmap的高效加载

    1. Bitmap是如何加载的? BitmapFactory类提供了四类方法:
      1. decodeFile
      2. decodeResource
      3. decodeStream
      4. decodeByteArray
    2. 如何高效加载Bitmap?
      1. 采用BitmapFactory.Options按照一定的采样率来加载所需尺寸的图片,因为imageview所需的图片大小往往小于图片的原始尺寸。
      2. BitmapFactory.Options的inSampleSize参数,即采样率
    3. 获取采样率和高效加载的例子

    注意:将inJustDecodeBounds设置为true的时候,BitmapFactory只会解析图片的原始宽高信息,并不会真正的加载图片

    public Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {
        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);
    
        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    
        // Decode bitmap with inSampleSize set
        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;
        }
    
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        Log.d(TAG, "origin, w= " + width + " h=" + height);
        int inSampleSize = 1;
    
        if (height > reqHeight || width > reqWidth) {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
    
            // Calculate the largest inSampleSize value that is a power of 2 and
            // keeps both height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }
    
        Log.d(TAG, "sampleSize:" + inSampleSize);
        return inSampleSize;
    }
    

    12.2 Android中的缓存策略

    1. 缓存的主要是为了避免流量消耗。主要策略包含缓存的添加、获取和删除
    2. 最常用的缓存算法是LRU,核心是当缓存满时,会优先淘汰那些近期最少使用的缓存对象
    3. LRU算法的缓存:LruCache(内存缓存)和DiskLruCache(磁盘缓存)

    LruCache

    1. LruCache是泛型类,内部采用LinkedHashMap以强引用的方式存储外界的缓存对象;
    2. 当缓存满时,移除较早使用的缓存对象,然后再添加新的缓存对象
    3. LruCache是线程安全的
    4. LruCache的创建示例
    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
            int cacheSize = maxMemory / 8;
            mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
                @Override
                protected int sizeOf(String key, Bitmap bitmap) {
                    return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
                }
            };
    
    1. put和get的示例
    mMemoryCache.get(key)
    
    mMemoryCache.put(key,bitmap)
    

    DiskLruCache

    DiskLruCache的创建、缓存查找和缓存添加操作

    ImageLoader的实现

    1. 图片压缩功能实现——使用BitmapFactory.Options的inSampleSize

    2. 内存缓存和磁盘缓存的实现

      1. 在ImageLoader的构造方法中,建立好LruCache和DiskLruCache
      2. 在loadBitmapFromHttp中,将http下载的数据存储到DiskLruCache中,然后去调用loadBitmapFromDiskCache;
      3. 在loadBitmapFromDiskCache中,取出相应的Cache,使用图片压缩获得压缩后的bitmap,将bitmap放入LruCache中——addBitmapToMemoryCache(key,bitmap)。
      4. 这样经过2、3两步,即实现了获取Bitmap,又实现了内存、硬盘的双缓存
    3. 同步加载和异步加载接口的设计

      1. 同步加载,不能在主线程使用,首先尝试从LruCache中获取没,接着尝试从DiskLruCache中读取,最后才从网络中拉取。
      public Bitmap loadBitmap(String uri, int reqWidth, int reqHeight) {
          Bitmap bitmap = loadBitmapFromMemCache(uri);
          if (bitmap != null) {
              Log.d(TAG, "loadBitmapFromMemCache,url:" + uri);
              return bitmap;
          }
      
          try {
              bitmap = loadBitmapFromDiskCache(uri, reqWidth, reqHeight);
              if (bitmap != null) {
                  Log.d(TAG, "loadBitmapFromDisk,url:" + uri);
                  return bitmap;
              }
              bitmap = loadBitmapFromHttp(uri, reqWidth, reqHeight);
              Log.d(TAG, "loadBitmapFromHttp,url:" + uri);
          } catch (IOException e) {
              e.printStackTrace();
          }
      
          if (bitmap == null && !mIsDiskLruCacheCreated) {
              Log.w(TAG, "encounter error, DiskLruCache is not created.");
              bitmap = downloadBitmapFromUrl(uri);
          }
      
          return bitmap;
      }
      
      
      1. 异步加载: 首先尝试从LruCache中获取,如果读取成功则返回,否则会从线程池中去调用loadBitmap,加载成功后,封装成一个LoaderResult通过Handler发给主线程去显示。
      public void bindBitmap(final String uri, final ImageView imageView,
              final int reqWidth, final int reqHeight) {
          imageView.setTag(TAG_KEY_URI, uri);
          Bitmap bitmap = loadBitmapFromMemCache(uri);
          if (bitmap != null) {
              imageView.setImageBitmap(bitmap);
              return;
          }
      
          Runnable loadBitmapTask = new Runnable() {
      
              @Override
              public void run() {
                  Bitmap bitmap = loadBitmap(uri, reqWidth, reqHeight);
                  if (bitmap != null) {
                      LoaderResult result = new LoaderResult(imageView, uri, bitmap);
                      mMainHandler.obtainMessage(MESSAGE_POST_RESULT, result).sendToTarget();
                  }
              }
          };
          THREAD_POOL_EXECUTOR.execute(loadBitmapTask);
      }
      
      1. 异步加载导致的列表错位问题:可以在给ImageView设置图片之前检查url有没有改变,如果发生改变则不给设置图片。
    4. 优化列表的卡顿现象

      1. 不要在getView中执行耗时操作,不要在getView中直接加载图片,否则肯定会导致卡顿;
      2. 控制异步任务的执行频率:在列表滑动的时候停止加载图片,等列表停下来以后再加载图片;
      3. 使用硬件加速来解决莫名的卡顿问题,给Activity添加配置android:hardwareAccelerated="true"。

    12.3 几个开源Imageloader

    1. Glide 一般项目里用这个的多,还可以实现裁剪,模糊等效果
    2. Fresco

    相关文章

      网友评论

          本文标题:第12章 Bitmap的加载和Cache

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