Androidがbitmapを使うOOMの心得と解決策について

10713 ワード

Android開発は、2010年から現在まで独立してappを完成させることを学び、この4年間、バグの苦しみを何度も経験し、数回の残業訓練を受けた.しかし、自分がandroidをよく知っていると思っていたのに、最近あるプロジェクトでoomに振り回されて1週間、元の場所に戻って、自分の不足を知って、自分がこんな菜鳥だと感じました.
はい、くだらないことを言わないで、みんなはandroidを開発する時、メモリを解放する重要性に注意したり意識したりすることはめったにありません.画像のメモリ使用による潜在的な危機はまったく感じられません.
もしあなたのプログラムの中で、機能を使ったら、あなたは合格したandroid開発エンジニアとして、oomの潜在的な危機に注意する必要があります.1.インタフェースが多く、多くのインタフェースの背景画像が違います.
2.皮膚の交換機能に関し、多種の皮膚を定義し、皮膚の資源はcolorではなく画像資源である.
以上の2つの場合、メモリの合理的な解放に注意しないと、自分のプログラムのわけのわからないクラッシュに代価を払うことになります.
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は私たちのために10759 kのメモリを解放してくれました.これは素晴らしいです.この瞬間、あなたはプログラムの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を使用してインタフェースを描画します.
このような状況に遭遇すると、androidプログラマーの40%が間違っているかもしれません.私たちは間違いを把握できないところに基づいて、Activityのライフサイクルに従って背景を埋めたり、背景を解放したりしていると思っていますが、このような間違いが発生しています.
ネット上の資料の解釈では、あなたは理解できますが、具体的な問題を見つけることができません.
私はこの問題の苦痛を経験したことがあります.ネット上の解釈と解決方法を見て、頼りになるものが見つかりませんでした.私はこの問題に対して、ずっとあきらめないで、何度もテストと検証を経て、この問題が現れた場所は2つの可能性があります:状況1、あなたのAインタフェースの中でa.pngピクチャを使って、それからジャンプをして、finishはAインタフェースをして、finishの時、あなたは1つのコードを使ってメモリを解放しました:
		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

字面を見るのは簡単で、理解しやすいが、実際に問題の所在を見つけるのは容易なことではない.
もしかすると、あなたはこの瞬間に狂っているのではないでしょうか.潜在的な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();

ロゴcatの結果を見てみましょう
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の共有に帰して、転載を歓迎して、しかし作者の同意を得ないで必ずこの声明を保留しなければならなくて、しかも文章のページの明らかな位置で原文の接続を与えて、さもなくば法律の責任を追及する権利を保留します.