关于android 使用bitmap的OOM心得和解决方案

android开发,从2010年开始学习到现在的独立完成一个app,这漫长的四年,已经经历了很多次bug的折磨,无数次的加班训练。然而,自以为自己已经比较了解android了,却最近在一个项目上,因为oom而折腾了一个周,回到原地,认识了自己的不足,感觉自己是如此的菜鸟呀。

好了,不废话,大家在使用开发android的时候,很少会注意或者意识到释放内存的重要性,因为大家在使用过程中,涉及的图片资源不多,或者比较稳定,来回切换界面,图片也就那么几张或者使用的都是很小的图片,根本不会感觉到图片占用内存可能引发的潜在危机。

如果你的程序中,使用了一下功能,你作为一个合格的android开发工程师,你有必要,注意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 45ms05-07 06:55:39.340: I/dalvikvm-heap(6988): Grow heap (frag case) to 13.126MB for 3686416-byte allocation05-07 06:55:39.421: D/dalvikvm(6988): GC_CONCURRENT freed <1K, 5% free 13314K/13936K, paused 6ms+5ms, total 83ms05-07 06:55:39.600: D/dalvikvm(6988): GC_FOR_ALLOC freed <1K, 5% free 13314K/13936K, paused 44ms, total 44ms05-07 06:55:39.670: I/dalvikvm-heap(6988): Grow heap (frag case) to 19.377MB for 6554896-byte allocation05-07 06:55:39.790: D/dalvikvm(6988): GC_CONCURRENT freed 0K, 4% free 19715K/20340K, paused 7ms+20ms, total 114ms05-07 06:55:40.011: I/System.out(6988): onCreate05-07 06:55:41.760: D/dalvikvm(6988): GC_EXPLICIT freed 10759K, 56% free 9063K/20340K, paused 4ms+7ms, total 111ms05-07 06:55:41.821: D/dalvikvm(6988): GC_EXPLICIT freed <1K, 56% free 9062K/20340K, paused 4ms+7ms, total 62ms05-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 105ms05-07 07:00:37.310: D/dalvikvm(6988): GC_EXPLICIT freed 88K, 82% free 2604K/13936K, paused 3ms+8ms, total 59ms05-07 07:00:37.310: I/System.out(6988): onDestroy05-07 07:00:37.891: D/AndroidRuntime(6988): Shutting down VM05-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: main05-07 07:00:37.991: E/AndroidRuntime(6988): java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@417fc28005-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的生命周期来做填充背景和释放背景,却出现这样的错误。

网上的资料解释,你能理解,却还是无法找到具体的问题所在。

含泪播种的人一定能含笑收获。

关于android 使用bitmap的OOM心得和解决方案

相关文章:

你感兴趣的文章:

标签云: