美文网首页
Android学习笔记之Bitmap和Cache

Android学习笔记之Bitmap和Cache

作者: sssssss_ | 来源:发表于2018-11-30 14:56 被阅读0次

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

    • Bitmap的加载
    • Cache缓存策略
    • ImageLoader实现

    一、Bitmap的加载

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

    • BitmapFactory.decodeFile:文件系统
    • BitmapFactory.decodeResource:资源
    • BitmapFactory.decodeStream:输入流
    • BitmapFactory.decodeByteArray:字节数组

    注: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等等。一定要去学习这些优秀框架,要学习他们是怎么写的,从中提升自己的代码能力。

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


    二、缓存策略

    (一)缓存的概念

    • 缓存:就是将从服务器请求到的数据(Json,File)等保存到本地。

    (二)缓存的优势

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

    (三)缓存的使用场景

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

    (四)常用缓存策略

    • 缓存算法LRU(Least Recently Used):当缓存满时,会优先淘汰那些近期最少使用的缓存对象。
      • LruCache:实现内存缓存。
      • DiskLruCache:实现硬盘缓存。

    (五)LruCache

    • LruCache 类是一个线程安全的泛型类:内部采用一个LinkedHashMap强引用的方式存储外界的缓存对象,并提供getput方法来完成缓存的获取和添加操作,当缓存满时会移除较早使用的缓存对象,再添加新的缓存对象。
    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);
        }
    }
    
    • 实现原理:LinkedHashMap 利用一个双重链接链表来维护所有条目item。
    • 常用属性accessOrder:决定LinkedHashMap的链表顺序。
      • 值为true:以访问顺序维护链表。
      • 值为false:以插入的顺序维护链表。
    • 而LruCache利用是 accessOrder=true 时的LinkedHashMap实现LRU算法,使得最近访问的数据会在链表尾部,在容量溢出时,将链表头部的数据移除。

    注:几种引用的含义

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

    (六)DiskLruCache

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

    • DiskLruCache的使用步骤
      1. 通过DiskLruCache.open方法初始化创建对象。
      2. 通过Editor添加、Snapshot获取、remove删除实现对数据的操作。
      3. 调用 flush() 将数据写入磁盘。

    • 第一步:DiskLruCache.open方法初始化创建对象

    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){}
    
    • 第二步:DiskLruCache.Editor方法缓存添加
    • 第三步:DiskLruCache缓存查找

    三、ImageLoader的实现

    相关文章

      网友评论

          本文标题:Android学习笔记之Bitmap和Cache

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