Android学习笔记之Bitmap和Cache

2018-11-30  本文已影响0人  sssssss_

本章的学习内容有以下几点:


一、Bitmap的加载

(一)BitmapFactory类提供的四种加载图片的方法:

注:decodeFile()decodeResource()又间接调用 decodeStream()

(二)高效加载Bitmap

  1. 采用BitmapFactory.Options来加载所需尺寸的图片,就可以按一定采样率来加载缩小后的图片,将缩小后的图片在 ImageView 中显示,这样就能降低内存占用从而在一定程度上避免OOM,提高了 Bitmap 加载时的性能。

  2. 当采样率inSampleSize=K=2 的时候,采样后的图片其宽高均为原图大小的 1/K。图片的内存大小缩放比例为 1/(K^2)

  3. 采样率inSampleSize的取值范围为2的指数(1、2、4...),非 2 的指数向下取(例子:3取2)。

注意:根据图片宽高的实际大小&需要大小,而计算出的缩放比尽可能取最小,避免由于缩小的过多,导致在控件中不能铺满而被拉伸至模糊。

(三)有效加载图片的步骤

  1. 将 BitmapFactory.Options 的 inJustDecodeBounds 参数设为 true 进行解析图片宽高。
  2. 从 BitmapFactory.Options 的中取出图片的原始宽高
  3. 根据采样率的规则结合目标 View 的所需大小计算出采样率 inSampleSize
  4. 将 BitmapFactory.Options 的 inJustDecodeBounds 参数设为 false,然后重新加载图片。
/**
 * 对一个Resources的资源文件进行指定长宽来加载进内存, 并把这个bitmap对象返回
 * @param res       资源文件对象
 * @param resId     要操作的图片id
 * @param reqWidth  最终想要得到bitmap的宽度
 * @param reqHeight 最终想要得到bitmap的高度
 * @return 返回采样之后的bitmap对象
 */
public static Bitmap decodeFromResource(
        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);
}
/**
 * 计算采样率
 *
 * @param options   要操作的原始图片属性
 * @param reqWidth  最终想要得到bitmap的宽度
 * @param reqHeight 最终想要得到bitmap的高度
 * @return 返回采样率
 */
private static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    //获取原始图片宽高
    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;
        //计算缩放比,是2的指数
        while ((halfHeight / inSampleSize) >= reqHeight 
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }
    return inSampleSize;
}

执行代码的方法:

//通过这样的方式可以高效的加载并显示图片 
ImageView.setImageBitmap(decodeFromResource(getResources(),R.id.myImg,100,100));
  • 其实在Android中有许多优秀的图片加载开源框架,比如Glide、Picasso和Fresco等等。一定要去学习这些优秀框架,要学习他们是怎么写的,从中提升自己的代码能力。

  • 所以在这节的内容中只是了解怎么加载图片而已,真正重要的是怎么去做缓存


二、缓存策略

(一)缓存的概念

(二)缓存的优势

  1. 对一些不是经常发生变化的数据直接使用本地缓存,减少消耗用户的流量
  2. 不再频繁请求服务器,可以降低服务器的负载压力
  3. 可以在一些特殊场景下使用:离线使用

(三)缓存的使用场景

  1. 对 Bitmap 和 File 等大数据进行缓存,无需每次都下载,比如 ListView。
  2. 数据不需实时更新。

(四)常用缓存策略

(五)LruCache

public class LruCache<K, V> {
    private final LinkedHashMap<K, V> map;
    ...
}
//私有构造函数,用来初始化缓存对象
private SimpleImageLoader() {
    //1.获取当前进程的可用内存,转换成KB单位
    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
    //2.分配缓存的大小
    int cacheSize = maxMemory / 8;
    //3.创建LruCache对象并重写 sizeOf 方法
    mLruCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap bitmap) {
            //转换成KB,sizeOf完成Bitmap对象的大小计算
            return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
        }
    };
}
//4.从缓存中读取图片
private Bitmap getBitmapfromCache(String url) {
    return mLruCache.get(url);
}
//5.将下载的图片保存在缓存中
private void putBitmaptoCache(Bitmap bitmap, String url) {
    if (bitmap != null) {
        mLruCache.put(url, bitmap);
    }
}

注:几种引用的含义

  • 强引用:直接的对象引用,不会被gc回收;
  • 软引用:当系统内存不足时,对象会被gc回收;
  • 弱引用:随时会被gc回收;

(六)DiskLruCache

DiskLruCache mDiskLruCache = null;

protected void onCreate(Bundle savedInstanceState) {
    ...
    File diskCacheDir = getDiskCacheDir(mContext, "bitmap");
    if (!diskCacheDir.exists()) {
        diskCacheDir.mkdirs(); //若缓存地址的路径不存在就创建一个
    }
    //创建对象
    mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);
}

//用于获取到缓存地址的路径
public File getDiskCacheDir(Context context, String uniqueName) {
    String cachePath;
    if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
        //SD卡,获取路径 /sdcard/Android/data/<application package>/cache
        cachePath = context.getExternalCacheDir().getPath();
    } else {
        //手机本地,获取路径/data/data/<application package>/cache
        cachePath = context.getCacheDir().getPath();
    }
    return new File(cachePath + File.separator + uniqueName);
}
/**
 * @param directory  表示磁盘缓存在文件系统中的存储路径:SD卡和手机
 * @param appVersion  表示应用的版本号,一般设为1即可
 * @param valueCount 表示单个节点对应的数据的个数,1即可
 * @param maxSize    表示缓存的总大小,比如50MB,当缓存超出就会开始清理一部分
 * @return
 */
public static DiskLruCacheDemo open(File directory, int appVersion, int valueCount, long maxSize){}

三、ImageLoader的实现

上一篇下一篇

猜你喜欢

热点阅读