【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


目录
相关文章
|
3月前
|
安全 Java 应用服务中间件
Spring Boot + Java 21:内存减少 60%,启动速度提高 30% — 零代码
通过调整三个JVM和Spring Boot配置开关,无需重写代码即可显著优化Java应用性能:内存减少60%,启动速度提升30%。适用于所有在JVM上运行API的生产团队,低成本实现高效能。
296 3
|
3月前
|
存储 大数据 Unix
Python生成器 vs 迭代器:从内存到代码的深度解析
在Python中,处理大数据或无限序列时,迭代器与生成器可避免内存溢出。迭代器通过`__iter__`和`__next__`手动实现,控制灵活;生成器用`yield`自动实现,代码简洁、内存高效。生成器适合大文件读取、惰性计算等场景,是性能优化的关键工具。
241 2
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
安全 数据安全/隐私保护 Android开发
探索Android与iOS的隐私保护策略
在数字时代,智能手机已成为我们生活中不可或缺的一部分,而随之而来的则是对个人隐私和数据安全的日益关注。本文将深入探讨Android与iOS两大操作系统在隐私保护方面的策略和实践,分析它们如何应对日益严峻的隐私挑战,以及用户应如何保护自己的数据安全。通过对比分析,我们将揭示两大系统在隐私保护方面的优势和不足,为用户提供有价值的见解和建议。
|
存储 缓存 监控
后端开发中的缓存机制:深度解析与最佳实践####
本文深入探讨了后端开发中不可或缺的一环——缓存机制,旨在为读者提供一份详尽的指南,涵盖缓存的基本原理、常见类型(如内存缓存、磁盘缓存、分布式缓存等)、主流技术选型(Redis、Memcached、Ehcache等),以及在实际项目中如何根据业务需求设计并实施高效的缓存策略。不同于常规摘要的概述性质,本摘要直接点明文章将围绕“深度解析”与“最佳实践”两大核心展开,既适合初学者构建基础认知框架,也为有经验的开发者提供优化建议与实战技巧。 ####
|
12月前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
352 3
|
存储 算法 Java
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
281 5
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
312 6
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
423 1
|
11月前
|
安全 测试技术 数据库
代码危机:“内存溢出” 事件的深度剖析与反思
初涉编程时,我坚信严谨逻辑能让代码顺畅运行。然而,“内存溢出”这一恶魔却以残酷的方式给我上了一课。在开发电商平台订单系统时,随着订单量增加,系统逐渐出现处理迟缓甚至卡死的情况,最终排查发现是订单状态更新逻辑中的细微错误导致内存无法及时释放,进而引发内存溢出。这次经历让我深刻认识到微小错误可能带来巨大灾难,从此对待代码更加谨慎,并养成了定期审查和测试的习惯。
159 0

热门文章

最新文章