关于android 使用bitmap的OOM心得和解决方案
来源:互联网 发布:c语言文件 编辑:程序博客网 时间:2024/05/22 05:25
- 关于android 使用bitmap的OOM心得和解决方案
android开发,从2010年开始学习到现在的独立完成一个app,这漫长的四年,已经经历了很多次bug的折磨,无数次的加班训练。然而,自以为自己已经比较了解android了,却最近在一个项目上,因为oom而折腾了一个周,回到原地,认识了自己的不足,感觉自己是如此的菜鸟呀。好了,不废话,大家在使用开发android的时候,很少会注意或者意识到释放内存的重要性,因为大家在使用过程中,涉及的图片资源不多,或者比较稳定,来回切换界面,图片也就那么几张或者使用的都是很小的图片,根本不会感觉到图片占用内存可能引发的潜在危机。
如果你的程序中,使用了一下功能,你作为一个合格的android开发工程师,你有必要,注意oom的潜在危机
1.界面比较多,并且很多界面的背景图片不一样;2.涉及到换肤功能,定义多种皮肤,皮肤的资源不是使用color 而是图片资源;
以上两种情况,如果你不注意合理的释放内存,你将会为自己程序莫名其妙的崩溃付出代价的。我们在android程序中,无论是使用layout布局设置了背景还是使用了setBackgroundResource 设置背景,你都以为不需要释放内存?这绝对是一个错误的观念,绝对的错误。也许这一点的错误的认识,将会在大屏幕手机上,暴露出来你的oom的现象,特别是在三星的大屏幕手机,爆oom的几率更大。
也许,大家在使用开发中,会注意这一点,使用了一下方式去解决释放内存(网上很多例子都是使用该方式去释放内存),比如:
12345678910View 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 打印出来的结果
1234567891005
-
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
12345678910111213141516171819202122232425262728293031323334353637383940414205
-
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的时候,你使用了一下的代码释放了内存:12345678910View 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在内存中已经释放了,所以就会引发上面的105
-
07
07
:
00
:
37.991
: E/AndroidRuntime(
6988
): java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap
@417fc280
看着字面很简单,很容易理解,实际中找到问题的所在却不是容易的一件事。
也许,你在这一刻是否已经抓狂了,难道就没办法来释放内存来解决潜在的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();
以上的代码,我们修改为:1234View view = findViewById(R.id.page_bg);
view.setBackgroundResource(
0
);
System.gc();
我们在看看我们的logcat的结果:以上的界面和我们调用了手动释放的方式是不是一样? 呵呵,反复测试多次,看看还会不会导致java.lang.RuntimeException: Canvas: trying to use a recycled bitmap1234567891005
-
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
实际上,我们的程序不会再出现上面的问题了,并且解决了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心得和解决方案
- 关于android 使用bitmap的OOM心得和解决方案
- 关于android 使用bitmap的OOM心得和解决方案
- 关于android 使用bitmap的OOM心得和解决方案
- 关于android 使用bitmap的OOM心得和解决方案
- 关于android 使用bitmap的OOM心得和解决方案
- 关于android 使用bitmap的OOM心得和解决方案
- 关于android 使用bitmap的OOM心得和解决方案
- android使用bitmap的OOM心得
- android 关于listview bitmap导致的oom解决方案
- 关于Bitmap 导致的OOM 解决方案
- Android 关于OOM的解决方案
- Android 关于OOM的解决方案
- Android 关于OOM的解决方案
- Android 关于OOM的解决方案
- Android 关于OOM的解决方案
- Android 关于OOM的解决方案
- Android关于OOM的解决方案
- VMware WorkStation的三种网络连接方式
- 【苹果发布会】大了又大:苹果发布 iPhone 6 及 iPhone 6 Plus
- poj 3045 Cow Acrobats(数学题)
- VS 预生成事件命令行 取消文件只读
- 电商再赶“穿戴设备潮”易迅网推出智能手环
- 关于android 使用bitmap的OOM心得和解决方案
- SAP 日志管理
- 使用Tiled地图编辑器生成TMX地图文件
- Android之记录ListView的checkbox选择位置问题
- UITabBarController使用详解
- android布局的多个属性运用
- html url 编码 Url Encode 和 Url Decode 防止sql注入URL
- 分布式计算平台Hadoop 发展现状
- 笔记:Chrome中的 gpu 加速构建