关于android 使用bitmap的OOM心得和解决方式
android开发,从2010年開始学习到如今的独立完毕一个app,这漫长的四年,已经经历了非常多次bug的折磨。无数次的加班训练。然而,自以为自己已经比較了解android了,却近期在一个项目上。由于oom而折腾了一个周,回到原地。认识了自己的不足,感觉自己是如此的菜鸟呀。
好了,不废话,大家在使用开发android的时候,非常少会注意或者意识到释放内存的重要性。由于大家在使用过程中,涉及的图片资源不多,或者比較稳定,来回切换界面,图片也就那么几张或者使用的都是非常小的图片,根本不会感觉到图片占用内存可能引发的潜在危机。
假设你的程序中,使用了一下功能,你作为一个合格的android开发project师,你有必要。注意oom的潜在危机
1.界面比較多,而且非常多界面的背景图片不一样;
2.涉及到换肤功能。定义多种皮肤,皮肤的资源不是使用color 而是图片资源;
以上两种情况,假设你不注意合理的释放内存,你将会为自己程序莫名其妙的崩溃付出代价的。
我们在android程序中,不管是使用layout布局设置了背景还是使用了setBackgroundResource 设置背景。你都以为不须要释放内存?这绝对是一个错误的观念,绝对的错误。或许这一点的错误的认识,将会在大屏幕手机上。暴露出来你的oom的现象。特别是在三星的大屏幕手机。爆oom的几率更大。
或许。大家在使用开发中。会注意这一点,使用了一下方式去解决释放内存(网上非常多样例都是使用该方式去释放内存),比方:
- View view = findViewById(R.id.page_bg);
- BitmapDrawable bitmapDrawable = (BitmapDrawable) view.getBackground();
- view.setBackgroundResource(0);
- bitmapDrawable.setCallback(null);
- Bitmap bitmap = bitmapDrawable.getBitmap();
- if(bitmap != null && !bitmap.isRecycled()){
- bitmap.recycle();
- bitmap = null;
- }
- System.gc();
从我们的測试效果来看看logcat 打印出来的结果
- 05-07 06:55:39.330: D/dalvikvm(6988): GC_FOR_ALLOC freed 4091K, 31% free 9715K/13936K, paused 44ms, total 45ms
- 05-07 06:55:39.340: I/dalvikvm-heap(6988): Grow heap (frag case) to 13.126MB for 3686416-byte allocation
- 05-07 06:55:39.421: D/dalvikvm(6988): GC_CONCURRENT freed <1K, 5% free 13314K/13936K, paused 6ms+5ms, total 83ms
- 05-07 06:55:39.600: D/dalvikvm(6988): GC_FOR_ALLOC freed <1K, 5% free 13314K/13936K, paused 44ms, total 44ms
- 05-07 06:55:39.670: I/dalvikvm-heap(6988): Grow heap (frag case) to 19.377MB for 6554896-byte allocation
- 05-07 06:55:39.790: D/dalvikvm(6988): GC_CONCURRENT freed 0K, 4% free 19715K/20340K, paused 7ms+20ms, total 114ms
- 05-07 06:55:40.011: I/System.out(6988): onCreate
- 05-07 06:55:41.760: D/dalvikvm(6988): GC_EXPLICIT freed 10759K, 56% free 9063K/20340K, paused 4ms+7ms, total 111ms
- 05-07 06:55:41.821: D/dalvikvm(6988): GC_EXPLICIT freed <1K, 56% free 9062K/20340K, paused 4ms+7ms, total 62ms
- 05-07 06:55:41.821: I/System.out(6988): onDestroy
从以上logcat中。我们看到了GC_EXPLTCIT 为我们释放了10759k的内存,这个是非常可观的,或许这一刻你会沾沾自喜。会觉得你已经攻克了程序中oom的潜在危机。
然而,实际上,你却引发了另外一个潜在的问题,假设有A和B界面,同一时候使用了一个背景,你在A中释放了,在B中去使用。就会导致了一下error的logcat
- 05-07 07:00:37.250: D/dalvikvm(6988): GC_EXPLICIT freed 6411K, 81% free 2692K/13936K, paused 4ms+6ms, total 105ms
- 05-07 07:00:37.310: D/dalvikvm(6988): GC_EXPLICIT freed 88K, 82% free 2604K/13936K, paused 3ms+8ms, total 59ms
- 05-07 07:00:37.310: I/System.out(6988): onDestroy
- 05-07 07:00:37.891: D/AndroidRuntime(6988): Shutting down VM
- 05-07 07:00:37.891: W/dalvikvm(6988): threadid=1: thread exiting with uncaught exception (group=0x40a71930)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): FATAL EXCEPTION: main
- 05-07 07:00:37.991: E/AndroidRuntime(6988): java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@417fc280
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.graphics.Canvas.throwIfRecycled(Canvas.java:1026)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.graphics.Canvas.drawBitmap(Canvas.java:1127)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:393)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.View.draw(View.java:13697)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.View.draw(View.java:13596)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.ViewGroup.drawChild(ViewGroup.java:2928)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2797)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.View.draw(View.java:13594)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.ViewGroup.drawChild(ViewGroup.java:2928)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2797)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.View.draw(View.java:13594)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.ViewGroup.drawChild(ViewGroup.java:2928)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2797)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.View.draw(View.java:13715)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.widget.FrameLayout.draw(FrameLayout.java:467)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2211)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.ViewRootImpl.drawSoftware(ViewRootImpl.java:2281)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.ViewRootImpl.draw(ViewRootImpl.java:2177)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2045)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1854)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:989)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4351)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.Choreographer$CallbackRecord.run(Choreographer.java:749)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.Choreographer.doCallbacks(Choreographer.java:562)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.Choreographer.doFrame(Choreographer.java:532)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.os.Handler.handleCallback(Handler.java:725)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.os.Handler.dispatchMessage(Handler.java:92)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.os.Looper.loop(Looper.java:137)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at android.app.ActivityThread.main(ActivityThread.java:5041)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at java.lang.reflect.Method.invokeNative(Native Method)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at java.lang.reflect.Method.invoke(Method.java:511)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
- 05-07 07:00:37.991: E/AndroidRuntime(6988): at dalvik.system.NativeStart.main(Native Method)
感觉非常奇怪把。由于你在这句logcat中,根本找不到错误的地方和为什么发生这种错误,可是这个错误却是致命的。
从英文的字面意思理解:你使用了一个已经释放的Bitmap来绘制你的界面。
遇到这种情况,或许40%的android程序猿都不知所错,由于我们依据无法把握出错的地方,或许你觉得你是依照Activity的生命周期来做填充背景和释放背景,却出现这种错误。
网上的资料解释。你能理解,却还是无法找到详细的问题所在。
我经历过这个问题的痛苦,看了网上的解释和解决的方法,根本就找不到靠谱的。我对待这个问题。始终不放弃,经过了多次的測试和验证,该问题出现的地点是有一下两种可能性:
情况1、 你A界面中使用了a.png图片,然后做了跳转。finish了A界面,finish的时候,你使用了一下的代码释放了内存:
- View view = findViewById(R.id.page_bg);
- BitmapDrawable bitmapDrawable = (BitmapDrawable) view.getBackground();
- view.setBackgroundResource(0);
- bitmapDrawable.setCallback(null);
- Bitmap bitmap = bitmapDrawable.getBitmap();
- if(bitmap != null && !bitmap.isRecycled()){
- bitmap.recycle();
- bitmap = null;
- }
- System.gc();
然后再次进入A界面。极有可能出现该问题。
情况2、 你在A界面使用了a.png图片。然后在B界面也使用了a.png, A界面没有finish。B界面使用了finsh,而且使用上面相同的方式释放了内存。然后我们正常情况下。A界面会经历onResume的方式来显示,但是这个时候。我们的a.png在内存中已经释放了,所以就会引发上面的
- 05-07 07:00:37.991: E/AndroidRuntime(6988): java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@417fc280
看着字面非常简单。非常easy理解,实际中找到问题的所在却不是easy的一件事。
或许,你在这一刻是否已经抓狂了。难道就没办法来释放内存来解决潜在的oom问题?
答案是否。像google这种大公司。他们提供的api。考虑的潜在问题比我们正常程序猿考虑的还要深入N倍。
个人从新阅读了相关的api,得出了一下解决的方法来释放内存。解除我们潜在的oom情况
不管你是在xml中布局使用了:
android:background 。
还是在java代码中调用了:
setBackground(background);
setBackgroundDrawable(background)
setBackgroundResource(resid)
的方式去设置了背景图片.
使用的时候。请调用一下相应的方法:
setBackgroundResource和android:background →setBackgroundResource(0);
setBackgroundDrawable(background)→
setBackgroundDrawable(null)
setBackground(background)
→ setBackground(null)
然后再onDestory中调用System.gc();
以上的代码,我们改动为:
- View view = findViewById(R.id.page_bg);
- view.setBackgroundResource(0);
- System.gc();
我们在看看我们的logcat的结果:
- 05-07 07:29:18.941: D/dalvikvm(7598): GC_FOR_ALLOC freed 27K, 65% free 9791K/27452K, paused 43ms, total 58ms
- 05-07 07:29:18.970: I/dalvikvm-heap(7598): Grow heap (frag case) to 13.203MB for 3686416-byte allocation
- 05-07 07:29:19.041: D/dalvikvm(7598): GC_FOR_ALLOC freed 83K, 52% free 13308K/27452K, paused 72ms, total 72ms
- 05-07 07:29:19.140: D/dalvikvm(7598): GC_CONCURRENT freed <1K, 52% free 13308K/27452K, paused 4ms+29ms, total 105ms
- 05-07 07:29:19.240: D/dalvikvm(7598): GC_FOR_ALLOC freed <1K, 52% free 13307K/27452K, paused 40ms, total 40ms
- 05-07 07:29:19.310: I/dalvikvm-heap(7598): Grow heap (frag case) to 19.373MB for 6554896-byte allocation
- 05-07 07:29:19.420: D/dalvikvm(7598): GC_CONCURRENT freed 0K, 29% free 19709K/27452K, paused 4ms+31ms, total 106ms
- 05-07 07:29:19.590: I/System.out(7598): onCreate
- 05-07 07:29:21.290: D/dalvikvm(7598): GC_EXPLICIT freed 10749K, 56% free 9064K/20340K, paused 3ms+8ms, total 125ms
- 05-07 07:29:21.290: I/System.out(7598): onDestroy
以上的界面和我们调用了手动释放的方式是不是一样? 呵呵。重复測试多次,看看还会不会导致java.lang.RuntimeException: Canvas: trying to use a recycled bitmap
实际上,我们的程序不会再出现上面的问题了。而且攻克了oom得潜在危机。
如此的简单,如此的便捷。
同理:假设我们在程序中。使用了ImageView的话, 我们也能够使用
imageView.setImageResource(0); 这样的方式,来释放我们设置的android:src或者bitmap等等。
那么,关于图片的oom可能潜在的问题,我们告一段落,从这次的经历。让我更加的深入的了解java语言的内存回收机制,总结一句话:尽可能的让系统去释放内存。我们仅仅负责标示须要释放的内存。通俗点:我们仅仅是标记须要杀的人,谁来杀,就让杀手看着办。
作者:SpringSky
出处:http://blog.csdn.net/springsky_/article/details/25212419
blog:http://blog.csdn.net/springsky_
本文版权归作者和CSDN共同拥有,欢迎转载,但未经作者允许必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
关于android 使用bitmap的OOM心得和解决方式的更多相关文章
- Eclipse出现"Running Android Lint has encountered a problem"解决方式
近期打开Eclipse的时候,总是发生这种一个错误:"Running Android Lint has encountered a problem".截图例如以下: . 可是Ecl ...
- Android中不同方向嵌套滑动的解决方式(ListView为样例)
前言: 就像手机QQ的聊天消息列表.一个纵向滑动的ListView列举全部消息,但每一条消息能够横向滑动. 而默认情况下,仅仅能有一个地方消化处理触摸事件,要么ListView吃掉这个事件.要么子It ...
- android sudio 执行的中文是乱码解决方式
1.File-->Setings-->查找file encodings 例如以下图 2.将 IDE Encoding .Project Encoding.Default encoding ...
- Android真机调试不打印日志解决方式
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/RowandJJ/article/details/24543459 1.在拨号界面输入:*#*#28 ...
- android studio 非法字符: '\ufeff' 解决方式
今天发现一个问题,就是从其它地方拷贝的代码到AS项目里面,木有语法 错误,可是就是执行不起来,老是报错"非法字符: '\ufeff' ",郁闷非常久.木有看到这个字符.最后查询了这 ...
- Android Google Play app signing 最终完美解决方式
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/105561341 本文出自[赵彦军的博客] 在 GooglePlay 创建 App ...
- Android加载图片OOM错误解决方式
前几天做项目的时候,甲方要求是PAD (SAMSUNG P600 10.1寸 2560*1600)的PAD上显示高分辨率的大图片. SQLITE採用BOLD方式存储图片,这个存取过程就不说了哈,网上一 ...
- Android ListView异步载入图片乱序问题,原因分析及解决方式
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/45586553 在Android全部系统自带的控件其中,ListView这个控件算是 ...
- Android之——常见Bug及其解决方式
转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46942139 1.android.view.WindowManager$BadTo ...
随机推荐
- PHP开发中涉及到emoji表情的几种处理方法!
emoji表情 处理 一般Mysql表设计时,都是用UTF8字符集的.把带有emoji的昵称字段往里面insert一下就没了,整个字段变成了空字符串.这是怎么回事呢? 原来是因为Mysql的utf8字 ...
- python爬虫基础07-selenium大全1/8-安装和简单使用
Selenium笔记(1)安装和简单使用 本文集链接:https://www.jianshu.com/nb/25338984 简介 Selenium是一个用于Web应用程序测试的工具. Seleniu ...
- (转)iOS 常用宏定义
#ifndef MacroDefinition_h #define MacroDefinition_h //-------------------获取设备大小------------------- ...
- (原)剑指offer之位运算
题目描述 输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. 思路: count为1的位数,初始为零 每次右移一为,与1做与运算,结果不为零说明最后一位为1 c++代码如下 in ...
- bootstap 折叠
data-toggle="collapse" 添加到您想要展开或折叠的组件的链接上. href 或 data-target 属性添加到父组件,它的值是子组件的 id
- AVL树总结
定义:一棵AVL树或者是空树,或者是具有下列性质的二叉搜索树:它的左子树和右子树都是AVL树,且左右子树的高度之差的绝对值不超过1 AVL树失衡旋转总结: 假如以T为根的子树失衡.定义平衡因子为 H( ...
- 【18】什么是FOUC?如何避免
[18]什么是FOUC?如何避免 Flash Of Unstyled Content: 用户定义样式表加载之前浏览器使用默认样式显示文档,用户样式加载渲染之后再从新显示文档,造成页面闪烁. 解决方法: ...
- x86保护模式 任务状态段和控制门
x86保护模式 任务状态段和控制门 每个任务都有一个任务状态段TSS 用于保存任务的有关信息 在任务内权变和任务切换时 需要用到这些信息 任务内权变的转移和任务切换 一 ...
- Excel读取导入数据库碰到的问题
1.未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序. 下载并安装驱动:http://download.microsoft.com/download/7/0/3/703 ...
- hdu2024
这题目感觉不是很严谨,如果是关键字的话也是不能作为合法标识符的,但是这个不用检测,就算要检测也会很费劲,还得用字符串匹配,而且还得知道一共都有哪些关键字,太麻烦了,所以出题人原意就是检查大小写字母数字 ...