【Android 内存优化】Bitmap 硬盘缓存 ( Google 官方 Bitmap 示例 | DiskLruCache 开源库 | 代码示例 )(二)

简介: 【Android 内存优化】Bitmap 硬盘缓存 ( Google 官方 Bitmap 示例 | DiskLruCache 开源库 | 代码示例 )(二)

五、从磁盘缓存中读取数据


1 . 从 DiskLruCache 中获取 Bitmap 流程 :



① 获取快照 : 通过 key 获取 DiskLruCache.Snapshot 对象 ;


snapshot = mDiskLruCache.get(key);


② 打开编辑器 : 打开 DiskLruCache.Editor , 该用法与 SharedPreference 用法类似 ;


DiskLruCache.Editor editor = mDiskLruCache.edit(key);


③ 获取输入流 : 从 DiskLruCache.Editor 对象中获取出输入流 , 这里的 0 表示获取该 key 对应的第 0 个文件 , 每个 Key 可以对应多个文件 , 这个值是创建 DiskLruCache 时传入的 valueCount 参数 ;


inputStream = editor.newInputStream(0);


④ 从输入流中读取数据到 Bitmap 中 : 这里用到了 Bitmap 内存复用机制 , 另外从磁盘读取数据后 , 向内存缓存一份 ;


                 

BitmapFactory.Options options = new BitmapFactory.Options();
                    options.inMutable = true;
                    options.inBitmap = inBitmap;
                    // 写出 Bitmap 对象到文件中
                    bitmap = BitmapFactory.decodeStream(inputStream, null, options);
                    if(bitmap != null){
                        // 从磁盘读取后 , 先缓存到内存中
                        mLruCache.put(key, bitmap);
                    }


⑤ 编辑器提交数据 :


editor.commit();




2 . 代码示例 :


 

/**
     * 从 磁盘缓存 中取出 Bitmap 对象
     * @param key       键值
     * @param inBitmap 复用 Bitmap 内存
     * @return
     */
    public Bitmap getBitmapFromDisk(String key, Bitmap inBitmap){
        Bitmap bitmap = null;
        DiskLruCache.Snapshot snapshot = null;
        InputStream inputStream = null;
        try {
            snapshot = mDiskLruCache.get(key);
            // 如果缓存中有对应 key 键值的文件 , 不进行任何处理
            if(snapshot != null) {
                // 该用法与 SharedPreference 用法类似
                DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                if(editor != null){
                    // 这里的 0 表示获取该 key 对应的第 0 个文件
                    // 每个 可以 可以对应多个文件 , 这个值是创建 DiskLruCache 时传入的 valueCount 参数
                    inputStream = editor.newInputStream(0);
                    BitmapFactory.Options options = new BitmapFactory.Options();
                    options.inMutable = true;
                    options.inBitmap = inBitmap;
                    // 写出 Bitmap 对象到文件中
                    bitmap = BitmapFactory.decodeStream(inputStream, null, options);
                    if(bitmap != null){
                        // 从磁盘读取后 , 先缓存到内存中
                        mLruCache.put(key, bitmap);
                    }
                    // 该用法与 SharedPreference 用法类似
                    editor.commit();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(snapshot != null) {
                snapshot.close();
            }
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return bitmap;
    }





六、 Android 10 文件访问


文件存储相关官方参考资料 :


Android 11 中的存储机制更新

Android storage use cases and best practices

应用数据和文件


参考之前的博客 【Android 内存优化】Android 原生 API 图片压缩代码示例 ( PNG 格式压缩 | JPEG 格式压缩 | WEBP 格式压缩 | 动态权限申请 | Android10 存储策略 ) 三、 Android 10 文件访问 有涉及到在 Android 10 系统中访问 SD 卡 ;






七、代码示例




1、二级缓存代码示例


磁盘内存二级缓存代码示例 :


package kim.hsl.bm.utils;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.util.LruCache;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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;
import kim.hsl.bm.BuildConfig;
import kim.hsl.bm.diskcache.DiskLruCache;
/**
 * Bitmap 内存缓存
 * 在将图片缓存到 LruCache 内存中基础上 ,
 * 将从 LruCache 中移除的最近没有使用的 Bitmap 对象的内存复用
 * 这样能最大限度减少内存抖动
 */
public class BitmapDiskLruCacheMemoryReuse {
    private static final String TAG = "BitmapMemoryCache";
    /**
     * 应用上下文对象
     */
    private Context mContext;
    /**
     * 缓存图片的 LruCache
     */
    private LruCache<String, Bitmap> mLruCache;
    /**
     * 磁盘缓存
     */
    private DiskLruCache mDiskLruCache;
    /**
     * 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 BitmapDiskLruCacheMemoryReuse INSTANCE;
    private BitmapDiskLruCacheMemoryReuse(){}
    public static BitmapDiskLruCacheMemoryReuse getInstance(){
        if(INSTANCE == null){
            INSTANCE = new BitmapDiskLruCacheMemoryReuse();
        }
        return INSTANCE;
    }
    /**
     * 使用时初始化
     * @param context
     */
    public void init(Context context, String diskDirectory){
        // 初始化内存缓存
        initLruCache(context);
        // 初始化弱引用队列
        initBitmapReusePool();
        // 初始化磁盘缓存
        initDiskLruCache(diskDirectory);
    }
    /**
     * 不使用时释放
     */
    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();
    }
    /**
     * 初始化磁盘缓存
     * @param diskDirectory
     */
    private void initDiskLruCache(String diskDirectory){
        try {
            /*
                初始化内存缓存
                需要传入内存缓存目录文件
                APP 版本
                缓存值的个数
                缓存大小 , 单位字节 , 这个最重要
             */
            mDiskLruCache = DiskLruCache.open(
                    new File(diskDirectory),
                    BuildConfig.VERSION_CODE,
                    1,
                    8 * 1024 * 10024
            );
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取一个可以被复用的 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 个方法是提供给用户用于操作 磁盘 的接口
     */
    /**
     * 将 Bitmap 放入 磁盘缓存 中
     * @param key
     * @param bitmap
     */
    public void putBitmapToDisk(String key, Bitmap bitmap){
        DiskLruCache.Snapshot snapshot = null;
        OutputStream outputStream = null;
        try {
            snapshot = mDiskLruCache.get(key);
            // 如果缓存中有对应 key 键值的文件 , 不进行任何处理
            if(snapshot != null) {
                // 该用法与 SharedPreference 用法类似
                DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                if(editor != null){
                    // 这里的 0 表示获取该 key 对应的第 0 个文件
                    // 每个 可以 可以对应多个文件 , 这个值是创建 DiskLruCache 时传入的 valueCount 参数
                    outputStream = editor.newOutputStream(0);
                    // 写出 Bitmap 对象到文件中
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 0, outputStream);
                    // 该用法与 SharedPreference 用法类似
                    editor.commit();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(snapshot != null) {
                snapshot.close();
            }
            if(outputStream != null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    /**
     * 从 磁盘缓存 中取出 Bitmap 对象
     * @param key       键值
     * @param inBitmap 复用 Bitmap 内存
     * @return
     */
    public Bitmap getBitmapFromDisk(String key, Bitmap inBitmap){
        Bitmap bitmap = null;
        DiskLruCache.Snapshot snapshot = null;
        InputStream inputStream = null;
        try {
            snapshot = mDiskLruCache.get(key);
            // 如果缓存中有对应 key 键值的文件 , 不进行任何处理
            if(snapshot != null) {
                // 该用法与 SharedPreference 用法类似
                DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                if(editor != null){
                    // 这里的 0 表示获取该 key 对应的第 0 个文件
                    // 每个 可以 可以对应多个文件 , 这个值是创建 DiskLruCache 时传入的 valueCount 参数
                    inputStream = editor.newInputStream(0);
                    BitmapFactory.Options options = new BitmapFactory.Options();
                    options.inMutable = true;
                    options.inBitmap = inBitmap;
                    // 写出 Bitmap 对象到文件中
                    bitmap = BitmapFactory.decodeStream(inputStream, null, options);
                    if(bitmap != null){
                        // 从磁盘读取后 , 先缓存到内存中
                        mLruCache.put(key, bitmap);
                    }
                    // 该用法与 SharedPreference 用法类似
                    editor.commit();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(snapshot != null) {
                snapshot.close();
            }
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return bitmap;
    }
}






2、调用工具类代码示例


/**
     * 图像磁盘内存缓存
     */
    private void diskMemoryCache(){
        // 初始化 LruCache 内存缓存 , 与引用队列 , 一般在 onCreate 方法中初始化
        // 这里为了演示 , 放在方法的开头位置
        BitmapDiskLruCacheMemoryReuse.getInstance().init(this, Environment.getExternalStorageDirectory() + "/diskCache");
        // 1. 第一次尝试从 LruCache 内存中获取 Bitmap 数据
        Bitmap bitmap = BitmapDiskLruCacheMemoryReuse.getInstance().
                getBitmapFromLruCache(R.drawable.blog + "");
        /*
            如果从内存中获取 Bitmap 对象失败 , 再次从磁盘中尝试获取该 Bitmap
         */
        if(bitmap == null){
            // 要复用内存的 Bitmap 对象 , 将新的 Bitmap 写入到该 Bitmap 内存中
            Bitmap inBitmap = null;
            // 尝试获取复用对象
            BitmapDiskLruCacheMemoryReuse.getInstance().
                    getReuseBitmap(200, 200, 1);
            // 2. 第二次尝试从磁盘中获取图片
            bitmap = BitmapDiskLruCacheMemoryReuse.getInstance().getBitmapFromDisk(
                    R.drawable.blog + "", inBitmap);
            // 磁盘中没有找到 , 再次尝试加载该图片
            if(bitmap == null) {
                // 3. 如果内存, 磁盘都没有获取到 Bitmap, 那么加载指定大小格式的图像
                bitmap = BitmapSizeReduce.getResizedBitmap(this, R.drawable.blog,
                        200, 200, false, inBitmap);
                // 将新的 bitap 放入 LruCache 内存缓存中
                BitmapDiskLruCacheMemoryReuse.getInstance().
                        putBitmapToLruCache(R.drawable.blog + "", bitmap);
            }
        }
    }







八、源码及资源下载


源码及资源下载地址 :


① GitHub 工程地址 : BitmapMemory


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


目录
相关文章
|
20天前
|
存储 缓存 监控
|
24天前
|
存储 JavaScript 前端开发
如何优化代码以避免闭包引起的内存泄露
本文介绍了闭包引起内存泄露的原因,并提供了几种优化代码的策略,帮助开发者有效避免内存泄露问题,提升应用性能。
|
1月前
|
存储 缓存 API
LangChain-18 Caching 将回答内容进行缓存 可在内存中或数据库中持久化缓存
LangChain-18 Caching 将回答内容进行缓存 可在内存中或数据库中持久化缓存
42 6
|
3月前
|
存储 缓存 JSON
一行代码,我优化掉了1G内存占用
这里一行代码,指的是:String.intern()的调用,为了调用这一行代码,也写了几十行额外的代码。
|
3月前
|
缓存 Java
Java内存管理秘籍:掌握强软弱幻四大引用,让代码效率翻倍!
【8月更文挑战第29天】在Java中,引用是连接对象与内存的桥梁,主要分为强引用、软引用、弱引用和幻象引用。强引用确保对象生命周期由引用控制,适用于普通对象;软引用在内存不足时可被回收,适合用于内存敏感的缓存;弱引用在无强引用时即可被回收,适用于弱关联如监听器列表;幻象引用需与引用队列配合使用,用于跟踪对象回收状态,适用于执行清理工作。合理使用不同类型的引用车可以提升程序性能和资源管理效率。
46 4
|
3月前
|
前端开发 JavaScript Java
揭开 JavaScript 垃圾回收的秘密——一场与内存泄漏的生死较量,让你的代码从此焕然一新!
【8月更文挑战第23天】本文通过多个实例深入探讨了JavaScript中的垃圾回收机制及其对应用性能的影响。首先介绍了基本的内存管理方式,随后分析了变量不再使用时的回收过程。接着,通过事件监听器未被移除及全局变量管理不当等场景展示了常见的内存泄漏问题。最后,文章介绍了使用`WeakRef`和`FinalizationRegistry`等现代API来有效避免内存泄漏的方法。理解并运用这些技术能显著提升Web应用的稳定性和效率。
91 0
|
3月前
|
缓存 程序员
封装一个给 .NET Framework 用的内存缓存帮助类
封装一个给 .NET Framework 用的内存缓存帮助类
|
3月前
|
缓存 开发框架 .NET
看看 Asp.net core Webapi 项目如何优雅地使用内存缓存
看看 Asp.net core Webapi 项目如何优雅地使用内存缓存
|
3月前
|
缓存 编解码 测试技术
使用Go实现健壮的内存型缓存
使用Go实现健壮的内存型缓存
68 2
|
4月前
|
存储 数据库 Android开发
🔥Android Jetpack全解析!拥抱Google官方库,让你的开发之旅更加顺畅无阻!🚀
【7月更文挑战第28天】在Android开发中追求高效稳定的路径?Android Jetpack作为Google官方库集合,是你的理想选择。它包含多个独立又协同工作的库,覆盖UI到安全性等多个领域,旨在减少样板代码,提高开发效率与应用质量。Jetpack核心组件如LiveData、ViewModel、Room等简化了数据绑定、状态保存及数据库操作。引入Jetpack只需在`build.gradle`中添加依赖。例如,使用Room进行数据库操作变得异常简单,从定义实体到实现CRUD操作,一切尽在掌握之中。拥抱Jetpack,提升开发效率,构建高质量应用!
70 4