07-07 10:59:14.519: ERROR/AndroidRuntime(20429): FATAL EXCEPTION: main
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:470)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:284)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:309)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at android.graphics.drawable.Drawable.createFromPath(Drawable.java:800)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at android.app.ActivityThread.access$1500(ActivityThread.java:117)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at android.os.Handler.dispatchMessage(Handler.java:99)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at android.os.Looper.loop(Looper.java:130)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at android.app.ActivityThread.main(ActivityThread.java:3683)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at java.lang.reflect.Method.invokeNative(Native Method)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at java.lang.reflect.Method.invoke(Method.java:507)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
07-07 10:59:14.519: ERROR/AndroidRuntime(20429): at dalvik.system.NativeStart.main(Native Method)
This error was stemming from the large bitmaps I was switching out between activities. When I monitored the Heap Size for my app in the DDMS I was always well under the 16MB cap when the error occurred. I started manually running the garbage collector and even started scaling down my images to no avail. I found that this issue had been reported (Issue 8488) and promptly declined. In the bug report comments you can see that this issue is highly debated whether this is a bug or working as expected.
The best explanation I could find was that the Dalvik heap is used for your Java memory allocations and the Native heap is used for other things like your bitmaps. The DDMS is only reporting the Dalvik heap size. So when switching between activities and setting a new bitmap to an ImageView source I was inflating the total heap size beyond the cap. I was never nulling out the ImageView source references therefore eventually running out of memory.
I also found a nice solution in the comments for this issue. Here is a "BetterActivity" class that extends Activity. This handles nulling out all of the ImageViews to release the memory and running the garbage collector.
1: import android.app.Activity;
2: import android.view.LayoutInflater;
3: import android.view.View;
4: import android.view.ViewGroup;
5: import android.view.ViewGroup.LayoutParams;
6: import android.widget.ImageView;
7:
8: public abstract class BetterActivity extends Activity
9: {
10: private ViewGroup mContentView = null;
11:
12: @Override
13: protected void onResume()
14: {
15: System.gc();
16: super.onResume();
17: }
18:
19: @Override
20: protected void onPause()
21: {
22: super.onPause();
23: System.gc();
24: }
25:
26: @Override
27: public void setContentView(int layoutResID)
28: {
29: ViewGroup mainView = (ViewGroup)LayoutInflater.from(this).inflate(layoutResID, null);
30: setContentView(mainView);
31: }
32:
33: @Override
34: public void setContentView(View view)
35: {
36: super.setContentView(view);
37: mContentView = (ViewGroup)view;
38: }
39:
40: @Override
41: public void setContentView(View view, LayoutParams params)
42: {
43: super.setContentView(view, params);
44: mContentView = (ViewGroup)view;
45: }
46:
47: @Override
48: protected void onDestroy()
49: {
50: super.onDestroy();
51:
52: nullViewDrawablesRecursive(mContentView);
53: mContentView = null;
54: System.gc();
55: }
56:
57: private void nullViewDrawablesRecursive(View view)
58: {
59: if(view != null)
60: {
61: try
62: {
63: ViewGroup viewGroup = (ViewGroup)view;
64:
65: int childCount = viewGroup.getChildCount();
66:
67: for(int index = 0; index < childCount; index++)
68: {
69: View child = viewGroup.getChildAt(index);
70: nullViewDrawablesRecursive(child);
71: }
72: }
73: catch(Exception e){}
74:
75: nullViewDrawable(view);
76: }
77: }
78:
79: private void nullViewDrawable(View view)
80: {
81: try
82: {
83: view.setBackgroundDrawable(null);
84: }
85: catch(Exception e){}
86:
87: try
88: {
89: ImageView imageView = (ImageView)view;
90: imageView.setImageDrawable(null);
91: imageView.setBackgroundDrawable(null);
92: }
93: catch(Exception e){}
94: }
95: }
96: