当前位置:

首页 > 系统应用 > 8.2.2 Bitmap引起的OOM问题

8.2.2 Bitmap引起的OOM问题

本节引言: 上节,我们已经学习了Bitmap的基本用法,而本节我们要来探讨的Bitmap的OOM问题, 大家在实际开发中可能遇到过,或者没遇到过因为Bitmap引起的OOM问题,本节我们 就来围绕这个话题来进行学习~了解什么是OOM,为什么会引起OOM,改善因Bitmap引起的 OOM问题~ 1.什

本文目录

    本节引言:

    上节,我们已经学习了Bitmap的基本用法,而本节我们要来探讨的Bitmap的OOM问题, 大家在实际开发中可能遇到过,或者没遇到过因为Bitmap引起的OOM问题,本节我们 就来围绕这个话题来进行学习~了解什么是OOM,为什么会引起OOM,改善因Bitmap引起的 OOM问题~

    1.什么是OOM?为什么会引起OOM?

    答:Out Of Memory(内存溢出),我们都知道Android系统会为每个APP分配一个独立的工作空间, 或者说分配一个单独的Dalvik虚拟机,这样每个APP都可以独立运行而不相互影响!而Android对于每个 Dalvik虚拟机都会有一个最大内存限制,如果当前占用的内存加上我们申请的内存资源超过了这个限制 ,系统就会抛出OOM错误!另外,这里别和RAM混淆了,即时当前RAM中剩余的内存有1G多,但是OOM还是会发生!别把RAM(物理内存)和OOM扯到一起!另外RAM不足的话,就是杀应用了,而不是仅仅是OOM了! 而这个Dalvik中的最大内存标准,不同的机型是不一样的,可以调用:

    ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
    Log.e("HEHE","最大内存:" + activityManager.getMemoryClass());

    获得正常的最大内存标准,又或者直接在命令行键入:

    adb shell getprop | grep dalvik.vm.heapgrowthlimit

    你也可以打开系统源码/system/build.prop文件,看下文件中这一部分的信息得出:

    dalvik.vm.heapstartsize=8m
    dalvik.vm.heapgrowthlimit=192m
    dalvik.vm.heapsize=512m
    dalvik.vm.heaptargetutilization=0.75
    dalvik.vm.heapminfree=2m
    dalvik.vm.heapmaxfree=8m

    我们关注的地方有三个:heapstartsize堆内存的初始大小,heapgrowthlimit标准的应用的最大堆 内存大小,heapsize则是设置了使用android:largeHeap的应用的最大堆内存大小!

    我这里试了下手头几个机型的正常最大内存分配标准:

    你也可以试试自己手头的机子~

    好啦,不扯了,关于OOM问题的产生,就扯到这里,再扯就到内存管理那一块了,可是个大块头, 现在还啃不动...下面我们来看下避免Bitmap OOM的一些技巧吧!

    2.避免Bitmap引起的OOM技巧小结

    1)采用低内存占用量的编码方式

    上一节说了BitmapFactory.Options这个类,我们可以设置下其中的inPreferredConfig属性, 默认是Bitmap.Config.ARGB_8888,我们可以修改成Bitmap.Config.ARGB_4444
    Bitmap.Config ARGB_4444:每个像素占四位,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位
    Bitmap.Config ARGB_8888:每个像素占八位,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位
    默认使用ARGB_8888,即一个像素占4个字节!

    2)图片压缩

    还是这个BitmapFactory.Options,通常会用inSampleSize来控制缩放比例。比如设为2,图片的长和宽都会变成原来的1/2,整体面积也就只剩原来的1/4;如果不需要缩放,直接设为1就可以。不过,压缩并不是越狠越好,这个值如果取得过小,图片很容易发糊;同时还得尽量避开拉伸变形的问题。所以更稳妥的做法,是在程序里动态计算出一个合适的inSampleSize。好在Options里还提供了一个很实用的参数:inJustDecodeBounds。把它设为true之后,decodeFiel并不会真正分配内存,但依然可以先拿到原始图片的尺寸信息。接着通过options.outWidth/outHeight读取图片宽高,再配合一定的算法,就能算出更合适的inSampleSize。这里感谢街神提供的代码——摘自鸿洋blog!

    public static int caculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        int width = options.outWidth;
        int height = options.outHeight;
        int inSampleSize = 1;
        if (width > reqWidth || height > reqHeight) {
            int widthRadio = Math.round(width * 1.0f / reqWidth);
            int heightRadio = Math.round(height * 1.0f / reqHeight);
            inSampleSize = Math.max(widthRadio, heightRadio);
        }
        return inSampleSize;
    }

    然后使用下上述的方法即可:

    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true; // 设置了此属性一定要记得将值设置为false
    Bitmap bitmap = null;
    bitmap = BitmapFactory.decodeFile(url, options);
    options.inSampleSize = computeSampleSize(options,128,128);
    options.inPreferredConfig = Bitmap.Config.ARGB_4444;
    /* 下面两个字段需要组合使用 */  
    options.inPurgeable = true;
    options.inInputShareable = true;
    options.inJustDecodeBounds = false;
    try {
        bitmap = BitmapFactory.decodeFile(url, options);
    } catch (OutOfMemoryError e) {
            Log.e(TAG, "OutOfMemoryError");
    }

    3.及时回收图像

    如果代码里持有了大量Bitmap对象,但应用本身并不需要在同一时刻把所有图片都展示出来,那就该及时把暂时用不上的Bitmap回收掉。尤其是在那些图片使用时机非常明确的场景里,完全可以主动调用recycle来释放资源。比如引导页图片,用完就回收;再比如帧动画,加载一张、绘制一张、释放一张,这样更利落。原则其实很直接:要显示时再加载,不显示时就及时置null或者recycle。像这样处理:imageView.setImageResource(0); 不过也要注意,某些场景下可能会出现同一张图片反复加载、释放、再加载的情况,这样一来,效率反而会被拖慢。

    4.其他方法

    下面这些方法,我并没有用过,大家可以自行查阅相关资料:

    1.简单通过SoftReference引用方式管理图片资源

    建个SoftReference的hashmap 使用图片时先查询这个hashmap是否有softreference, softreference里的图片是否为空, 如果为空就加载图片到softreference并加入hashmap。 无需再代码里显式的处理图片的回收与释放,gc会自动处理资源的释放。 这种方式处理起来简单实用,能一定程度上避免前一种方法反复加载释放的低效率。但还不够优化。

    示例代码:

    private Map> imageMap 
                                               = new HashMap>();
    public Bitmap loadBitmap(final String imageUrl,final ImageCallBack imageCallBack) {
        SoftReference reference = imageMap.get(imageUrl);
        if(reference != null) {
            if(reference.get() != null) {
                return reference.get();
            }
        }
        final Handler handler = new Handler() {
            public void handleMessage(final android.os.Message msg) {
                //加入到缓存中
                Bitmap bitmap = (Bitmap)msg.obj;
                imageMap.put(imageUrl, new SoftReference(bitmap));
                if(imageCallBack != null) {
                    imageCallBack.getBitmap(bitmap);
                }
            }
        };
        new Thread(){
            public void run() {
                Message message = handler.obtainMessage();
                message.obj = downloadBitmap(imageUrl);
                handler.sendMessage(message);
            }
        }.start();
        return null ;
    }
    // 从网上下载图片
    private Bitmap downloadBitmap (String imageUrl) {
        Bitmap bitmap = null;
        try {
            bitmap = BitmapFactory.decodeStream(new URL(imageUrl).openStream());
            return bitmap ;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } 
    }
    public interface ImageCallBack{
        void getBitmap(Bitmap bitmap);
    }

    2.LruCache + sd的缓存方式

    Android 3.1版本起,官方还提供了LruCache来进行cache处理,当存储Image的大小大于LruCache 设定的值,那么近期使用次数最少的图片就会被回收掉,系统会自动释放内存!

    使用示例:

    步骤:

    1)要先设置缓存图片的内存大小,我这里设置为手机内存的1/8, 手机内存的获取方式:int MAXMEMONRY = (int) (Runtime.getRuntime() .maxMemory() / 1024);

    2)LruCache里面的键值对分别是URL和对应的图片

    3)重写了一个叫做sizeOf的方法,返回的是图片数量。

    private LruCache mMemoryCache;
    private LruCacheUtils() {
        if (mMemoryCache == null)
            mMemoryCache = new LruCache(
                    MAXMEMONRY / 8) {
                @Override
                protected int sizeOf(String key, Bitmap bitmap) {
                    // 重写此方法来衡量每张图片的大小,默认返回图片数量。
                    return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
                }
    @Override
                protected void entryRemoved(boolean evicted, String key,
                        Bitmap oldValue, Bitmap newValue) {
                    Log.v("tag", "hard cache is full , push to soft cache");
    }
            };
    }

    4)下面的方法分别是清空缓存、添加图片到缓存、从缓存中取得图片、从缓存中移除。

    移除和清除缓存是必须要做的事,因为图片缓存处理不当就会报内存溢出,所以一定要引起注意。

    public void clearCache() {
        if (mMemoryCache != null) {
            if (mMemoryCache.size() > 0) {
                Log.d("CacheUtils",
                        "mMemoryCache.size() " + mMemoryCache.size());
                mMemoryCache.evictAll();
                Log.d("CacheUtils", "mMemoryCache.size()" + mMemoryCache.size());
            }
            mMemoryCache = null;
        }
    }
    public synchronized void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (mMemoryCache.get(key) == null) {
            if (key != null && bitmap != null)
                mMemoryCache.put(key, bitmap);
        } else
            Log.w(TAG, "the res is aready exits");
    }
    public synchronized Bitmap getBitmapFromMemCache(String key) {
        Bitmap bm = mMemoryCache.get(key);
        if (key != null) {
            return bm;
        }
        return null;
    }
    /**
     * 移除缓存
     * 
     * @param key
     */
    public synchronized void removeImageCache(String key) {
        if (key != null) {
            if (mMemoryCache != null) {
                Bitmap bm = mMemoryCache.remove(key);
                if (bm != null)
                    bm.recycle();
            }
        }
    }

    上述内容摘自——图片缓存之内存缓存技术LruCache,软引用

    本节小结:

    本节给大家讲解了OOM问题的发生缘由,也总结了一下网上给出的一些避免因Bitmap而引起OOM 的一些方案,因为公司做的APP都是地图类的,很少涉及到图片,所以笔者并没有遇到过OOM的问题, 所以对此并不怎么熟悉~后续在进阶课程的内存管理,我们再慢慢纠结这个OOM的问题,好的, 本节就到这里,谢谢~

    参考文献: Android应用中OOM问题剖析和解决方案

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    系统应用
    相关文章 更多
    Yandex官网入口及国际版登录指南
    Yandex官网入口及国际版登录指南

    Yandex官方网站快速入口是https://www.yondex.net/,该平台提供多语言搜索引擎、新闻聚合、翻译工具、图片视频检索及个性化推荐等核心服务,并集成地图、云存储、邮箱、日历等实用功能,支持移动端适配与夜间模式,界面简洁且加载迅速。

    Yandex官网入口及最新访问地址
    Yandex官网入口及最新访问地址

    Yandex搜索引擎官方入口是https://www.yondex.net/,该平台具备强大的多语言搜索能力、集成化的服务生态体系以及智能化的搜索体验优化功能。

    Yandex国际版入口及官网链接
    Yandex国际版入口及官网链接

    Yandex国际版官网为https://www.yondex.net/,提供英文界面,适合非俄语用户;2.https://www.yondex.net/为全球短域名,可自动跳转至对应地区版本;3.俄罗斯本地用户使用https://www.yondex.net/,内容以俄语为主,本地化程度高;4.普通用户可根据语言和需求选择相应入口。

    Yandex官网入口及网页版访问教程
    Yandex官网入口及网页版访问教程

    Yandex官方在线入口是https://www.yondex.net/,提供网页搜索、新闻、图片视频检索、地图导航等服务,并集成翻译、邮箱、云存储、日历等工具,支持个性化首页定制与电商比价功能。

    妖精漫画官网登录入口及首页指南
    妖精漫画官网登录入口及首页指南

    妖精漫画官网登录入口为https://www.yaojingmh.com。该平台资源丰富,涵盖多种题材,更新及时,分类清晰,支持多模式阅读与个性化收藏,具备评论、书架、弹幕等互动功能,界面简洁流畅,适配移动端,提供良好阅读体验。

    Yandex官网入口及最新搜索平台访问方式
    Yandex官网入口及最新搜索平台访问方式

    Yandex搜索平台最新访问入口是https://www.yondex.net/,该网站界面简洁,支持多语言切换和智能提示,具备强大的自然语言处理与多样化内容检索功能,并集成翻译、云存储、邮件等附加服务。

    Yandex搜索引擎官网入口及登录教程
    Yandex搜索引擎官网入口及登录教程

    俄罗斯Yandex搜索引擎创建于1993年,是俄语区主导的搜索服务,官网为https://www.yondex.net/,用户可直接访问并切换语言,无需注册即可使用其搜索、地图、邮箱等集成服务。

    Yandex浏览器官网登录与在线体验入口
    Yandex浏览器官网登录与在线体验入口

    Yandex浏览器官方网站登录入口是https://www.yondex.net/,提供多语言下载、账户同步、广告拦截、Turbo模式及隐私保护等功能,支持跨设备数据同步与安全浏览。

    2026年AO3镜像最新地址及稳定入口
    2026年AO3镜像最新地址及稳定入口

    ao3镜像2026最新入口包括主站archiveofourown.org、镜像站ao3mirror.com和xiaozhan.icu,建议收藏备用并根据网络情况切换使用。

    Win10搜索不到蓝牙耳机的排查与连接方法
    Win10搜索不到蓝牙耳机的排查与连接方法

    面向在Windows 10系统中无法发现蓝牙耳机的用户,本文提供从耳机配对模式到系统设置的完整排查路径。首先确保耳机已充电并进入可被搜索的配对状态,断开与其他设备的连接;随后在Win10设置中开启蓝牙并添加设备。若蓝牙开关缺失或适配器异常,需通过设备管理器检查驱动状态并更新,以恢复正常的蓝牙音频设备识别功能。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。