【Android 内存优化】Bitmap 内存缓存 ( Bitmap 内存复用 | 弱引用 | 引用队列 | 针对不同 Android 版本开发不同的 Bitmap 复用策略 | 工具类代码 )

简介: 【Android 内存优化】Bitmap 内存缓存 ( Bitmap 内存复用 | 弱引用 | 引用队列 | 针对不同 Android 版本开发不同的 Bitmap 复用策略 | 工具类代码 )

文章目录

一、Bitmap 复用池

二、弱引用 Bitmap 内存释放

三、从 Bitmap 复用池中获取对应可以被复用的 Bitmap 对象

1、Android 2.3.3(API 级别 10)及以下的版本

2、Android 4.4(API 级别 19)以下的版本

2、在 Android 4.4(API 级别 19)及以上的版本

四、LruCache 内存缓存、内存复用工具类

1、工具类

2、工具类测试

3、执行结果

五、源码及资源下载



在上一篇博客 【Android 内存优化】Bitmap 内存缓存 ( Bitmap 缓存策略 | LruCache 内存缓存 | LruCache 常用操作 | 工具类代码 ) 中 , 使用 LruCache 缓存 Bitmap 数据到内存中 , 设置其最大缓存为应用可用内存的 1/8 , 将解码后的 Bitmap 对象缓存到 LruCache 中 , 避免重复使用该 Bitmap 对象时重复解码加载图片 ;






一、Bitmap 复用池


1 . Bitmap 复用池 : 加载图片时 , 使用 inBitmap 复用选项 , 需要获取图片时 , 优先从 Bitmap 复用池中查找复用已存在的 Bitmap 对象 ; 假如 Bitmap 对象长时间不使用 , 就会从 LruCache 内存缓存中移除 , 此时放入到 Bitmap 复用池中 ;



2 . 弱引用 : 这里使用弱引用保存该 Bitmap , 每次 GC 时都会回收没有被引用的 Bitmap , 需要创建一个线程安全的 HashSet , 其中的元素是 Bitmap 弱引用 ;


Set<WeakReference<Bitmap>> bitmapReusePool;






二、弱引用 Bitmap 内存释放


有一点特别注意 , Java 中的弱引用 , 在 GC 时会回收没有使用到的内存 ; Bitmap 内存如果在 Java 层 , 可以将该内存回收 , 但是如果 Bitmap 内存在 Native 层 , 必须调用 Bitmap 对象的 recycle 方法 , 才能将内存释放 ;



1 . Bitmap 内存放置策略 :


3.0 以下系统中 , Bitmap 内存在 Native 层

3.0 以上系统中 , Bitmap 内存在 Java 层

8.0 及以上的系统中 , Bitmap 内存在 Native 层

为了适配所有手机 , 所有版本 , 不管 GC 是否自动释放 Bitmap 内存 , 在弱引用对象被回收时 , 必须手动调用一下 Bitmap 对象的 recycle 方法 ;



2 . 兼容弱引用释放方法 : 使用引用队列 ReferenceQueue 监控该弱引用 Bitmap 的 Set 集合元素 , 当有 Bitmap 被回收后 , 就会将其放入 ReferenceQueue 中 , 此时开启一个线程 , 不断从 ReferenceQueue 调用 remove 方法获取被释放的内存对象 , 如果获取到了非空内容 , 说明有一个 Bitmap 弱引用对象被释放了 , 拿到该对象引用 Reference 后 , 获取其对应的 Bitmap 对象 , 手动调用 Bitmap 对象的 recycle 方法 , 即可完成对应操作 ;


代码示例 :


/**
     * Bitmap 复用池
     * 使用 inBitmap 复用选项
     * 需要获取图片时 , 优先从 Bitmap 复用池中查找
     * 这里使用弱引用保存该 Bitmap , 每次 GC 时都会回收该 Bitmap
     * 创建一个线程安全的 HashSet , 其中的元素是 Bitmap 弱引用
     *
     * 该 Bitmap 复用池的作用是 , 假如 Bitmap 对象长时间不使用 , 就会从内存缓存中移除
     *
     * 因此这里需要处理 Bitmap 内存在 Native 层的情况 , 监控到 Java 层的弱引用被释放了
     * 需要调用 Bitmap 对象的 recycle 方法 , 释放 Native 层的内存
     *
     * 需要使用引用队列监控弱引用的释放情况
     */
    Set<WeakReference<Bitmap>> bitmapReusePool;
    /**
     * 引用队列 , 用于监控 Set<WeakReference<Bitmap>> bitmapReusePool 的内存是否被回收
     * 需要维护一个线程 , 不断尝试从该引用队列中获取引用
     *
     */
    private ReferenceQueue<Bitmap> referenceQueue;
    /**
     * 监控 Set<WeakReference<Bitmap>> bitmapReusePool 的内存是否被回收 ,
     * 调用 ReferenceQueue<Bitmap> referenceQueue 的 remove 方法 ,
     * 查看是否存在被回收的弱引用 , 如果存在 , 直接回收该弱引用对应的 Bitmap 对象
     */
    private Thread referenceQueueMonitorThread;
    /**
     * 是否持续监控引用队列 ReferenceQueue
     */
    private boolean isMonitorReferenceQueue = true;
    /**
     * 初始化引用队列
     */
    private void initBitmapReusePool(){
        // 创建一个线程安全的 HashSet , 其中的元素是 Bitmap 弱引用
        bitmapReusePool = Collections.synchronizedSet(new HashSet<WeakReference<Bitmap>>());
        // 引用队列 , 当弱引用被 GC 扫描后 , 需要回收 , 会将该弱引用放入队列
        // 一直不断的尝试从该引用队列中获取数据 , 如果获取到数据 , 就要回收该对象
        referenceQueue = new ReferenceQueue<>();
        // 定义监控线程
        referenceQueueMonitorThread = new Thread(){
            @Override
            public void run() {
                while (isMonitorReferenceQueue){
                    try {
                        Reference<Bitmap> reference = (Reference<Bitmap>) referenceQueue.remove();
                        Bitmap bitmap = reference.get();
                        // 不为空 , 且没有被回收 , 回收 Bitmap 内存
                        if(bitmap != null && !bitmap.isRecycled()){
                            bitmap.recycle();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        // 启动引用队列监控线程
        referenceQueueMonitorThread.start();
    }






三、从 Bitmap 复用池中获取对应可以被复用的 Bitmap 对象


根据不同系统版本进行不同处理 :




1、Android 2.3.3(API 级别 10)及以下的版本


Android 2.3.3(API 级别 10)及以下的版本 : 使用 Bitmap 对象的 recycle 方法回收内存 ;
        // Android 2.3.3(API 级别 10)及以下的版本中 , 使用 Bitmap 对象的 recycle 方法回收内存



2、Android 4.4(API 级别 19)以下的版本


Android 4.4(API 级别 19)以下的版本 : 复用的前提是必须同时满足以下 3 个条件 :


被解码的图像必须是 JPEG 或 PNG 格式

被复用的图像宽高必须等于 解码后的图像宽高

解码图像的 BitmapFactory.Options.inSampleSize 设置为 1 , 也就是不能缩放

才能复用成功 , 另外被复用的图像的像素格式 Config ( 如 RGB_565 ) 会覆盖设置的 BitmapFactory.Options.inPreferredConfig 参数 ;


 

if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2){
                    /*
                        Android 4.4(API 级别 19)以下的版本 : 在 Android 4.4(API 级别 19) 之前的代码中 ,
                        复用的前提是必须同时满足以下 3 个条件 :
                            1. 被解码的图像必须是 JPEG 或 PNG 格式
                            2. 被复用的图像宽高必须等于 解码后的图像宽高
                            3. 解码图像的 BitmapFactory.Options.inSampleSize 设置为 1 , 也就是不能缩放
                        才能复用成功 , 另外被复用的图像的像素格式 Config ( 如 RGB_565 ) 会覆盖设置的
                        BitmapFactory.Options.inPreferredConfig 参数 ;
                     */
                    if(bitmap.getWidth() == width &&
                            bitmap.getHeight() == height && //被复用的图像宽高必须等于 解码后的图像宽高
                            inSampleSize == 1){// 图像的 BitmapFactory.Options.inSampleSize 设置为 1
                        //符合要求
                        inBitmap = bitmap;
                        iterator.remove();
                    }
                }



2、在 Android 4.4(API 级别 19)及以上的版本


在 Android 4.4(API 级别 19)及以上的版本 : 只要被解码后的 Bitmap 对象的字节大小 , 小于等于 inBitmap 的字节大小 , 就可以复用成功 ; 解码后的乳香可以是缩小后的 , 即 BitmapFactory.Options.inSampleSize 可以大于1 ;
                }else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
                    /*
                        在 Android 4.4(API 级别 19)及以上的版本中 ,
                        只要被解码后的 Bitmap 对象的字节大小 , 小于等于 inBitmap 的字节大小 , 就可以复用成功 ;
                        解码后的乳香可以是缩小后的 , 即 BitmapFactory.Options.inSampleSize 可以大于1 ;
                     */
                    // 首先要计算图像的内存占用 , 先要计算出图像的宽高 , 如果图像需要缩放 , 计算缩放后的宽高
                    if(inSampleSize > 1){
                        width = width / inSampleSize ;
                        height = height / inSampleSize;
                    }
                    // 计算内存占用 , 默认 ARGB_8888 格式
                    int byteInMemory = width * height * 4;;
                    if(bitmap.getConfig() == Bitmap.Config.ARGB_8888){
                        // 此时每个像素占 4 字节
                        byteInMemory = width * height * 4;
                    }else if(bitmap.getConfig() == Bitmap.Config.RGB_565){
                        // 此时每个像素占 2 字节
                        byteInMemory = width * height * 2;
                    }
                    // 如果解码后的图片内存小于等于被复用的内存大小 , 可以复用
                    if(byteInMemory <= bitmap.getAllocationByteCount()){
                        //符合要求
                        inBitmap = bitmap;
                        iterator.remove();
                    }
                }





四、LruCache 内存缓存、内存复用工具类




1、工具类


BitmapLruCacheMemoryReuse.java 工具类地址 : BitmapLruCacheMemoryReuse.java
package kim.hsl.bm.utils;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
import android.util.LruCache;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/**
 * Bitmap 内存缓存
 * 在将图片缓存到 LruCache 内存中基础上 ,
 * 将从 LruCache 中移除的最近没有使用的 Bitmap 对象的内存复用
 * 这样能最大限度减少内存抖动
 */
public class BitmapLruCacheMemoryReuse {
    private static final String TAG = "BitmapMemoryCache";
    /**
     * 应用上下文对象
     */
    private Context mContext;
    /**
     * 缓存图片的 LruCache
     */
    private LruCache<String, Bitmap> mLruCache;
    /**
     * Bitmap 复用池
     * 使用 inBitmap 复用选项
     * 需要获取图片时 , 优先从 Bitmap 复用池中查找
     * 这里使用弱引用保存该 Bitmap , 每次 GC 时都会回收该 Bitmap
     * 创建一个线程安全的 HashSet , 其中的元素是 Bitmap 弱引用
     *
     * 该 Bitmap 复用池的作用是 , 假如 Bitmap 对象长时间不使用 , 就会从内存缓存中移除
     *
     * Bitmap 回收策略 :
     * 3.0 以下系统中 , Bitmap 内存在 Native 层
     * 3.0 以上系统中 , Bitmap 内存在 Java 层
     * 8.0 及以上的系统中 , Bitmap 内存在 Native 层
     *
     * 因此这里需要处理 Bitmap 内存在 Native 层的情况 , 监控到 Java 层的弱引用被释放了
     * 需要调用 Bitmap 对象的 recycle 方法 , 释放 Native 层的内存
     *
     * 需要使用引用队列监控弱引用的释放情况
     */
    Set<WeakReference<Bitmap>> bitmapReusePool;
    /**
     * 引用队列 , 用于监控 Set<WeakReference<Bitmap>> bitmapReusePool 的内存是否被回收
     * 需要维护一个线程 , 不断尝试从该引用队列中获取引用
     *
     */
    private ReferenceQueue<Bitmap> referenceQueue;
    /**
     * 监控 Set<WeakReference<Bitmap>> bitmapReusePool 的内存是否被回收 ,
     * 调用 ReferenceQueue<Bitmap> referenceQueue 的 remove 方法 ,
     * 查看是否存在被回收的弱引用 , 如果存在 , 直接回收该弱引用对应的 Bitmap 对象
     */
    private Thread referenceQueueMonitorThread;
    /**
     * 是否持续监控引用队列 ReferenceQueue
     */
    private boolean isMonitorReferenceQueue = true;
    /**
     * 单例实现
     */
    private static BitmapLruCacheMemoryReuse INSTANCE;
    private BitmapLruCacheMemoryReuse(){}
    public static BitmapLruCacheMemoryReuse getInstance(){
        if(INSTANCE == null){
            INSTANCE = new BitmapLruCacheMemoryReuse();
        }
        return INSTANCE;
    }
    /**
     * 使用时初始化
     * @param context
     */
    public void init(Context context){
        // 初始化内存缓存
        initLruCache(context);
        // 初始化弱引用队列
        initBitmapReusePool();
    }
    /**
     * 不使用时释放
     */
    public void release(){
        isMonitorReferenceQueue = false;
    }
    private void initLruCache(Context context){
        // 为成员变量赋值
        this.mContext = context;
        // 获取 Activity 管理器
        ActivityManager activityManager = (ActivityManager) context.getSystemService(
                Context.ACTIVITY_SERVICE);
        // 获取应用可用的最大内存
        int maxMemory = activityManager.getMemoryClass();
        // 获取的 maxMemory 单位是 MB , 将其转为字节 , 除以 8
        int lruCacheMemoryByte = maxMemory / 8 * 1024 * 1024;
        // 设置的内存 , 一般是 APP 可用内存的 1/8
        mLruCache = new LruCache<String, Bitmap>(lruCacheMemoryByte){
            /**
             * 返回 LruCache 的键和值的大小 , 单位使用用户自定义的单位
             * 默认的实现中 , 返回 1 ; size 是 键值对个数 , 最大的 size 大小是最多键值对个数
             * 键值对条目在 LruCache 中缓存时 , 其大小不能改变
             * @param key
             * @param value
             * @return 返回 LruCache<String, Bitmap> 的值 , 即 Bitmap 占用内存
             */
            @Override
            protected int sizeOf(String key, Bitmap value) {
                // 如果使用的是复用的 Bitmap 对象 , 其占用内存大小是之前的图像分配的内存大小
                // 大于等于当前图像的内存占用大小
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    return value.getAllocationByteCount();
                }
                return value.getByteCount();
            }
            /**
             * 从 LruCache 缓存移除 Bitmap 时会回调该方法
             * @param evicted
             * @param key
             * @param oldValue
             * @param newValue
             */
            @Override
            protected void entryRemoved(boolean evicted, String key, Bitmap oldValue,
                                        Bitmap newValue) {
                super.entryRemoved(evicted, key, oldValue, newValue);
                /*
                    如果从 LruCache 内存缓存中移除的 Bitmap 是可变的
                    才能被复用 , 否则只能回收该 Bitmap 对象
                    Bitmap 回收策略 :
                    3.0 以下系统中 , Bitmap 内存在 Native 层
                    3.0 以上系统中 , Bitmap 内存在 Java 层
                    8.0 及以上的系统中 , Bitmap 内存在 Native 层
                    因此这里需要处理 Bitmap 内存在 Native 层的情况 , 监控到 Java 层的弱引用被释放了
                    需要调用 Bitmap 对象的 recycle 方法 , 释放 Native 层的内存
                 */
                if(oldValue.isMutable()){   // 可以被复用
                    // 将其放入弱引用中 , 每次 GC 启动后 , 如果该弱引用没有被使用 , 都会被回收
                    bitmapReusePool.add(new WeakReference<Bitmap>(oldValue, referenceQueue));
                }else{  // 不可被复用 , 直接回收
                    oldValue.recycle();
                }
            }
        };
    }
    private void initBitmapReusePool(){
        // 创建一个线程安全的 HashSet , 其中的元素是 Bitmap 弱引用
        bitmapReusePool = Collections.synchronizedSet(new HashSet<WeakReference<Bitmap>>());
        // 引用队列 , 当弱引用被 GC 扫描后 , 需要回收 , 会将该弱引用放入队列
        // 一直不断的尝试从该引用队列中获取数据 , 如果获取到数据 , 就要回收该对象
        referenceQueue = new ReferenceQueue<>();
        // 定义监控线程
        referenceQueueMonitorThread = new Thread(){
            @Override
            public void run() {
                while (isMonitorReferenceQueue){
                    try {
                        Reference<Bitmap> reference = (Reference<Bitmap>) referenceQueue.remove();
                        Bitmap bitmap = reference.get();
                        // 不为空 , 且没有被回收 , 回收 Bitmap 内存
                        if(bitmap != null && !bitmap.isRecycled()){
                            bitmap.recycle();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        // 启动引用队列监控线程
        referenceQueueMonitorThread.start();
    }
    /**
     * 获取一个可以被复用的 Bitmap 对象
     *
     * 与 BitmapFactory 配合使用 :
     *
     * Android 4.4 以后的 Bitmap 复用情况 :
     * 在 KITKAT ( Android 4.4 , 19 平台 ) 以后的代码中 ,
     * 只要被解码生成的 Bitmap 对象的字节大小 ( 缩放后的 )
     * 小于等于 inBitmap 的字节大小 , 就可以复用成功 ;
     *
     * Android 4.4 之前的 Bitmap 复用情况 : ( 比较苛刻 )
     * 在 KITKAT 之前的代码中 , 被解码的图像必须是
     *  - JPEG 或 PNG 格式 ,
     *  - 并且 图像大小必须是相等的 ,
     *  - inssampleSize 设置为 1 ,
     * 才能复用成功 ;
     * 另外被复用的图像的 像素格式 Config ( 如 RGB_565 ) 会覆盖设置的 inPreferredConfig 参数
     *
     * @param width
     * @param height
     * @param inSampleSize
     * @return
     */
    public Bitmap getReuseBitmap(int width,int height,int inSampleSize){
        // Android 2.3.3(API 级别 10)及以下的版本中 , 使用 Bitmap 对象的 recycle 方法回收内存
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1){
            // 如果 API 级别小于等于 10 , 不启用 Bitmap 内存复用机制 , 返回 null 即可
            return null;
        }
        // 获取准备复用的 Bitmap , 之后设置到 Options 中
        Bitmap inBitmap = null;
        // 使用迭代器遍历该 Set 集合 , 如果遍历中涉及到删除 , 就要使用迭代器遍历
        Iterator<WeakReference<Bitmap>> iterator = bitmapReusePool.iterator();
        //迭代查找符合复用条件的Bitmap
        while (iterator.hasNext()){
            // 循环遍历 Bitmap 对象
            Bitmap bitmap = iterator.next().get();
            if (bitmap != null){
                /*
                    检查该 Bitmap 对象是否可以达到复用要求 ,
                    如果达到复用要求 , 就取出这个 Bitmap 对象 , 并将其从队列中移除
                 */
                if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2){
                    /*
                        Android 4.4(API 级别 19)以下的版本 : 在 Android 4.4(API 级别 19) 之前的代码中 ,
                        复用的前提是必须同时满足以下 3 个条件 :
                            1. 被解码的图像必须是 JPEG 或 PNG 格式
                            2. 被复用的图像宽高必须等于 解码后的图像宽高
                            3. 解码图像的 BitmapFactory.Options.inSampleSize 设置为 1 , 也就是不能缩放
                        才能复用成功 , 另外被复用的图像的像素格式 Config ( 如 RGB_565 ) 会覆盖设置的
                        BitmapFactory.Options.inPreferredConfig 参数 ;
                     */
                    if(bitmap.getWidth() == width &&
                            bitmap.getHeight() == height && //被复用的图像宽高必须等于 解码后的图像宽高
                            inSampleSize == 1){// 图像的 BitmapFactory.Options.inSampleSize 设置为 1
                        //符合要求
                        inBitmap = bitmap;
                        iterator.remove();
                    }
                }else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
                    /*
                        在 Android 4.4(API 级别 19)及以上的版本中 ,
                        只要被解码后的 Bitmap 对象的字节大小 , 小于等于 inBitmap 的字节大小 , 就可以复用成功 ;
                        解码后的乳香可以是缩小后的 , 即 BitmapFactory.Options.inSampleSize 可以大于1 ;
                     */
                    // 首先要计算图像的内存占用 , 先要计算出图像的宽高 , 如果图像需要缩放 , 计算缩放后的宽高
                    if(inSampleSize > 1){
                        width = width / inSampleSize ;
                        height = height / inSampleSize;
                    }
                    // 计算内存占用 , 默认 ARGB_8888 格式
                    int byteInMemory = width * height * 4;;
                    if(bitmap.getConfig() == Bitmap.Config.ARGB_8888){
                        // 此时每个像素占 4 字节
                        byteInMemory = width * height * 4;
                    }else if(bitmap.getConfig() == Bitmap.Config.RGB_565){
                        // 此时每个像素占 2 字节
                        byteInMemory = width * height * 2;
                    }
                    // 如果解码后的图片内存小于等于被复用的内存大小 , 可以复用
                    if(byteInMemory <= bitmap.getAllocationByteCount()){
                        //符合要求
                        inBitmap = bitmap;
                        iterator.remove();
                    }
                }
            }else if( bitmap == null ){
                // 如果 bitmap 为空 , 直接从复用 Bitmap 集合中移除
                iterator.remove();
            }
        }
        return inBitmap;
    }
    /*
        下面的 3 个方法是提供给用户用于操作 LruCache 的接口
     */
    /**
     * 将键值对放入 LruCache 中
     * @param key
     * @param value
     */
    public void putBitmapToLruCache(String key, Bitmap value){
        mLruCache.put(key, value);
    }
    /**
     * 从 LruCache 中获取 Bitmap 对象
     * @param key
     * @return
     */
    public Bitmap getBitmapFromLruCache(String key){
        return mLruCache.get(key);
    }
    /**
     * 清除 LruCache 缓存
     */
    public void clearLruCache(){
        mLruCache.evictAll();
    }
}



2、工具类测试


package kim.hsl.bm;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import kim.hsl.bm.utils.BitmapLruCacheMemoryReuse;
import kim.hsl.bm.utils.BitmapSizeReduce;
public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("native-lib");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv = findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
        // 内存缓存
        memoryCache();
    }
    /**
     * 图像缓存
     */
    private void memoryCache(){
        // 初始化 LruCache 内存缓存 , 与引用队列 , 一般在 onCreate 方法中初始化
        // 这里为了演示 , 放在方法的开头位置
        BitmapLruCacheMemoryReuse.getInstance().init(this);
        // 第一次从 LruCache 内存中获取 Bitmap 数据
        Bitmap bitmap = BitmapLruCacheMemoryReuse.getInstance().
                getBitmapFromLruCache(R.drawable.blog + "");
        /*
            如果从内存中获取 Bitmap 对象失败 , 这里就需要创建该图片 , 并放入 LruCache 内存中
         */
        if(bitmap == null){
            // 要复用内存的 Bitmap 对象 , 将新的 Bitmap 写入到该 Bitmap 内存中
            Bitmap inBitmap = null;
            // 尝试获取复用对象
            BitmapLruCacheMemoryReuse.getInstance().
                    getReuseBitmap(200, 200, 1);
            // 加载指定大小格式的图像
            bitmap = BitmapSizeReduce.getResizedBitmap(this, R.drawable.blog,
                    200, 200, false, inBitmap);
            // 将新的 bitap 放入 LruCache 内存缓存中
            BitmapLruCacheMemoryReuse.getInstance().
                    putBitmapToLruCache(R.drawable.blog + "", bitmap);
            Log.i("Bitmap 没有获取到创建新的", "blog : " + bitmap.getWidth() + " , " +
                    bitmap.getHeight() + " , " +
                    bitmap.getByteCount());
        }else{
            Log.i("Bitmap 内存中获取数据", "blog : " + bitmap.getWidth() + " , " +
                    bitmap.getHeight() + " , " +
                    bitmap.getByteCount());
        }
        // 第一次从 LruCache 内存中获取 Bitmap 数据
        Bitmap bitmap2 = BitmapLruCacheMemoryReuse.getInstance().
                getBitmapFromLruCache(R.drawable.blog + "");
        Log.i("Bitmap 第二次内存中获取数据", "blog : " + bitmap2.getWidth() + " , " +
                bitmap2.getHeight() + " , " +
                bitmap2.getByteCount());
    }




3、执行结果


执行结果 : 第一次尝试从 LruCache 中获取图像 , 没有获取到 , 创建新的 Bitmap 放入 LruCache 中 , 第二次获取直接从 LruCache 中获取到了图像 ;







五、源码及资源下载


源码及资源下载地址 :


① GitHub 工程地址 : BitmapMemory


② BitmapLruCacheMemoryReuse.java 工具类地址 : BitmapLruCacheMemoryReuse.java


目录
相关文章
|
1月前
|
缓存 算法 数据挖掘
深入理解缓存更新策略:从LRU到LFU
【10月更文挑战第7天】 在本文中,我们将探讨计算机系统中缓存机制的核心——缓存更新策略。缓存是提高数据检索速度的关键技术之一,无论是在硬件还是软件层面都扮演着重要角色。我们会详细介绍最常用的两种缓存算法:最近最少使用(LRU)和最少使用频率(LFU),并讨论它们的优缺点及适用场景。通过对比分析,旨在帮助读者更好地理解如何选择和实现适合自己需求的缓存策略,从而优化系统性能。
48 3
|
9天前
|
存储 分布式计算 算法
1GB内存挑战:高效处理40亿QQ号的策略
在面对如何处理40亿个QQ号仅用1GB内存的难题时,我们需要采用一些高效的数据结构和算法来优化内存使用。这个问题涉及到数据存储、查询和处理等多个方面,本文将分享一些实用的技术策略,帮助你在有限的内存资源下处理大规模数据集。
20 1
|
11天前
|
存储 监控 Java
深入理解计算机内存管理:优化策略与实践
深入理解计算机内存管理:优化策略与实践
|
22天前
|
存储 缓存 监控
利用 Redis 缓存特性避免缓存穿透的策略与方法
【10月更文挑战第23天】通过以上对利用 Redis 缓存特性避免缓存穿透的详细阐述,我们对这一策略有了更深入的理解。在实际应用中,我们需要根据具体情况灵活运用这些方法,并结合其他技术手段,共同保障系统的稳定和高效运行。同时,要不断关注 Redis 缓存特性的发展和变化,及时调整策略,以应对不断出现的新挑战。
56 10
|
18天前
|
Web App开发 缓存 UED
如何设置浏览器的缓存策略?
【10月更文挑战第23天】通过合理地设置浏览器的缓存策略,可以在提高网页性能、减少网络流量的同时,确保用户能够获取到最新的内容,从而提升用户体验和网站的性能优化效果。
54 4
|
19天前
|
存储 消息中间件 缓存
缓存策略
【10月更文挑战第25天】在实际应用中,还需要不断地监控和调整缓存策略,以适应系统的变化和发展。
|
22天前
|
缓存 监控 NoSQL
Redis 缓存穿透及其应对策略
【10月更文挑战第23天】通过以上对 Redis 缓存穿透的详细阐述,我们对这一问题有了更深入的理解。在实际应用中,我们需要根据具体情况综合运用多种方法来解决缓存穿透问题,以保障系统的稳定运行和高效性能。同时,要不断关注技术的发展和变化,及时调整策略,以应对不断出现的新挑战。
42 4
|
26天前
|
存储 缓存 NoSQL
保持HTTP会话状态:缓存策略与实践
保持HTTP会话状态:缓存策略与实践
|
1月前
|
存储 缓存 监控
|
1月前
|
缓存 分布式计算 NoSQL
大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念
大数据-47 Redis 缓存过期 淘汰删除策略 LRU LFU 基础概念
65 2