OutOfMemoryError when loading actions

I have a pretty simple activity:

public class SurvivalActivity extends Activity { private static final String KEY_LAYOUT_ID = "SurvivalLayoutId"; int mLayoutId; private static final int[] mSurvivalLayouts = { R.layout.survival1, R.layout.survival2, R.layout.survival3, }; @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Intent i = getIntent(); if (savedInstanceState != null && savedInstanceState.containsKey(KEY_LAYOUT_ID)) { mLayoutId = savedInstanceState.getInt(KEY_LAYOUT_ID, 0); } else if (i != null) { mLayoutId = i.getIntExtra(KEY_LAYOUT_ID, 0); } else { mLayoutId = 0; } // Layout setContentView(mSurvivalLayouts[mLayoutId]); // Title Util.setTitleFont(this, findViewById(R.id.title)); // "Next" button final View nextButton = findViewById(R.id.survival_next); Util.setFont(this, nextButton, "Lobster.ttf"); nextButton.setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { final Intent next = new Intent(SurvivalActivity.this, SurvivalActivity.class); next.putExtra(KEY_LAYOUT_ID, (mLayoutId + 1) % mSurvivalLayouts.length); startActivity(next); finish(); } }); } @Override protected void onSaveInstanceState(final Bundle outState) { outState.putInt(KEY_LAYOUT_ID, mLayoutId); } } 

Two static util methods. * just change the font of the TextView .

All 3 layouts are pretty simple, let me show you the first one:

 <?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" > <TextView style="@style/Title" android:text="@string/surv1_title" /> <TextView style="@style/Content" android:text="@string/surv1_1" /> <ImageView style="@style/Picture" android:src="@drawable/surv_crafting" /> <TextView style="@style/PicLegend" android:src="@string/surv_crafting_leg" /> <ImageView style="@style/Picture" android:src="@drawable/surv_hache" /> <TextView style="@style/PicLegend" android:src="@string/surv_hache_leg" /> <ImageView style="@style/Picture" android:src="@drawable/surv_pioche" /> <TextView style="@style/PicLegend" android:src="@string/surv_pioche_leg" /> <TextView style="@style/Content" android:text="@string/surv1_2" /> <ImageView style="@style/Picture" android:src="@drawable/surv_charbon" /> <TextView style="@style/PicLegend" android:src="@string/surv_charbon_leg" /> <TextView style="@style/Content" android:text="@string/surv1_3" /> <ImageView style="@style/Picture" android:src="@drawable/surv_torch" /> <TextView style="@style/PicLegend" android:src="@string/surv_torch_leg" /> <TextView style="@style/Content" android:text="@string/surv1_4" /> <ImageView style="@style/Picture" android:src="@drawable/surv_abri" /> <TextView style="@style/PicLegend" android:src="@string/surv_abri_leg" /> <TextView style="@style/Content" android:text="@string/surv1_5" /> <Button style="@style/SurvivalBoxNext" android:text="@string/surv1_next" /> </LinearLayout> </ScrollView> 

As you can see, there are several TextView with ImageView s and a Button that start loading the next action.

I run the application, it displays the first screen. I scroll down to the button, click, the second screen is displayed (with hiccups). I scroll down to the button, click and arrow.

 03-02 16:36:46.488 E/AndroidRuntime(10611): java.lang.RuntimeException: Unable to start activity ComponentInfo{net.bicou.myapp/net.bicou.myapp.SurvivalActivity}: android.view.InflateException: Binary XML file line #72: Error inflating class <unknown> 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1955) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1980) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.access$600(ActivityThread.java:122) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1146) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.os.Handler.dispatchMessage(Handler.java:99) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.os.Looper.loop(Looper.java:137) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.main(ActivityThread.java:4340) 03-02 16:36:46.488 E/AndroidRuntime(10611): at java.lang.reflect.Method.invokeNative(Native Method) 03-02 16:36:46.488 E/AndroidRuntime(10611): at java.lang.reflect.Method.invoke(Method.java:511) 03-02 16:36:46.488 E/AndroidRuntime(10611): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) 03-02 16:36:46.488 E/AndroidRuntime(10611): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) 03-02 16:36:46.488 E/AndroidRuntime(10611): at dalvik.system.NativeStart.main(Native Method) 03-02 16:36:46.488 E/AndroidRuntime(10611): Caused by: android.view.InflateException: Binary XML file line #72: Error inflating class <unknown> 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.createView(LayoutInflater.java:606) 03-02 16:36:46.488 E/AndroidRuntime(10611): at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.onCreateView(LayoutInflater.java:653) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:678) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.rInflate(LayoutInflater.java:739) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.rInflate(LayoutInflater.java:742) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.inflate(LayoutInflater.java:489) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.inflate(LayoutInflater.java:396) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.inflate(LayoutInflater.java:352) 03-02 16:36:46.488 E/AndroidRuntime(10611): at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:251) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.Activity.setContentView(Activity.java:1835) 03-02 16:36:46.488 E/AndroidRuntime(10611): at net.bicou.myapp.SurvivalActivity.onCreate(SurvivalActivity.java:34) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.Activity.performCreate(Activity.java:4465) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1919) 03-02 16:36:46.488 E/AndroidRuntime(10611): ... 11 more 03-02 16:36:46.488 E/AndroidRuntime(10611): Caused by: java.lang.reflect.InvocationTargetException 03-02 16:36:46.488 E/AndroidRuntime(10611): at java.lang.reflect.Constructor.constructNative(Native Method) 03-02 16:36:46.488 E/AndroidRuntime(10611): at java.lang.reflect.Constructor.newInstance(Constructor.java:417) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.view.LayoutInflater.createView(LayoutInflater.java:586) 03-02 16:36:46.488 E/AndroidRuntime(10611): ... 25 more 03-02 16:36:46.488 E/AndroidRuntime(10611): Caused by: java.lang.OutOfMemoryError 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.Bitmap.nativeCreate(Native Method) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.Bitmap.createBitmap(Bitmap.java:605) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.Bitmap.createBitmap(Bitmap.java:551) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:524) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:499) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:773) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.content.res.Resources.loadDrawable(Resources.java:1937) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.content.res.TypedArray.getDrawable(TypedArray.java:601) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.widget.ImageView.<init>(ImageView.java:119) 03-02 16:36:46.488 E/AndroidRuntime(10611): at android.widget.ImageView.<init>(ImageView.java:109) 03-02 16:36:46.488 E/AndroidRuntime(10611): ... 28 more 03-02 16:36:46.520 W/ActivityManager( 211): Force finishing activity net.bicou.myapp/.SurvivalActivity 

I do not understand:

  • Why the previous actions are not freed in order to free some space.
  • Why is my application taking up so much heap ( Grow heap (frag case) to 60.869MB for 3600016-byte allocation )
  • How to do to consistently display 3 actions containing about 10 TextView and 10 ImageView s
  • If I did something wrong, because I do not see anything wrong with my code, but obviously something is wrong.

Thanks.

Edit: image dimensions of the first action:

 -rw-r--r-- 1 bicou staff 167197 29 fév 16:42 surv_abri.jpg 600x377 px -rw-r--r-- 1 bicou staff 24658 29 fév 16:42 surv_charbon.png 559x277 px -rw-r--r-- 1 bicou staff 5285 29 fév 16:42 surv_crafting.png 214x121 px -rw-r--r-- 1 bicou staff 4190 29 fév 16:42 surv_hache.png 214x121 px -rw-r--r-- 1 bicou staff 3809 29 fév 16:42 surv_pioche.png 214x121 px -rw-r--r-- 1 bicou staff 2252 29 fév 16:42 surv_torch.png 215x121 px 

Second action:

 -rw-r--r-- 1 bicou staff 3371 29 fév 16:42 surv_four.png 204x112 px -rw-r--r-- 1 bicou staff 5059 29 fév 16:42 surv_glass.png 215x122 px -rw-r--r-- 1 bicou staff 3176 29 fév 16:42 surv_malle.png 204x112 px -rw-r--r-- 1 bicou staff 2676 29 fév 16:42 surv_pelle.png 204x112 px -rw-r--r-- 1 bicou staff 167166 29 fév 16:42 surv_repere.png 528x331 px -rw-r--r-- 1 bicou staff 134706 29 fév 16:42 surv_sand.jpg 854x480 px 

Third action (failure):

 -rw-r--r-- 1 bicou staff 58919 29 fév 16:42 surv_1.jpg 600x377 px -rw-r--r-- 1 bicou staff 51144 29 fév 16:42 surv_2.jpg 600x377 px -rw-r--r-- 1 bicou staff 46917 29 fév 16:42 surv_3.jpg 600x377 px -rw-r--r-- 1 bicou staff 53226 29 fév 16:42 surv_4.jpg 600x377 px -rw-r--r-- 1 bicou staff 37787 29 fév 16:42 surv_5.jpg 600x377 px -rw-r--r-- 1 bicou staff 31050 29 fév 16:42 surv_6.jpg 600x377 px -rw-r--r-- 1 bicou staff 38389 29 fév 16:42 surv_7.jpg 600x377 px -rw-r--r-- 1 bicou staff 44471 29 fév 16:42 surv_8.jpg 600x377 px 

And the background "Window":

 -rw-r--r-- 1 bicou staff 30857 29 fév 16:42 background_img.png 

EDIT:

OK. I tried to inflate the views directly from the code to see if I have an error. Here is my activity code (only + onCreate variables since the rest are identical)

 private static final int[] mTitles = { R.string.surv1_title, R.string.surv2_title, R.string.surv3_title }; private static final int[][] mTextViews = { { R.string.surv1_1, R.string.surv1_2, R.string.surv1_3, R.string.surv1_4, R.string.surv1_5 }, { R.string.surv2_1, R.string.surv2_2, R.string.surv2_3, R.string.surv2_4, R.string.surv2_5, R.string.surv2_6 }, { R.string.surv3_1, R.string.surv3_2, R.string.surv3_3 } }; private static final int[][] mImageViews = { { R.drawable.surv_pioche, R.drawable.surv_crafting, R.drawable.surv_hache, R.drawable.surv_charbon, R.drawable.surv_torch, R.drawable.surv_abri }, { R.drawable.surv_malle, R.drawable.surv_four, R.drawable.surv_pelle, R.drawable.surv_repere, R.drawable.surv_sand, R.drawable.surv_glass }, { R.drawable.surv_1, R.drawable.surv_2, R.drawable.surv_3, R.drawable.surv_4, R.drawable.surv_5, R.drawable.surv_6, R.drawable.surv_7, R.drawable.surv_8 }, }; private static final int[] mNextButtons = { R.string.surv1_next, R.string.surv2_next, R.string.surv3_next }; @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Intent i = getIntent(); if (savedInstanceState != null && savedInstanceState.containsKey(KEY_LAYOUT_ID)) { mLayoutId = savedInstanceState.getInt(KEY_LAYOUT_ID, 0); } else if (i != null) { mLayoutId = i.getIntExtra(KEY_LAYOUT_ID, 0); } else { mLayoutId = 0; } // Layout //setContentView(mSurvivalLayouts[mLayoutId]); LinearLayout ll; ScrollView sv; TextView tv; ImageView iv; Button b; sv = new ScrollView(this); sv.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); ll = new LinearLayout(this); ll.setOrientation(LinearLayout.VERTICAL); ll.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); tv = new TextView(this); tv.setId(R.id.title); tv.setText(getString(mTitles[mLayoutId])); ll.addView(tv); for (final int textId: mTextViews[mLayoutId]) { tv = new TextView(this); tv.setText(getString(textId)); ll.addView(tv); } for (final int imgId: mImageViews[mLayoutId]) { Li("Creating image: "+imgId); iv = new ImageView(this); iv.setImageResource(imgId); ll.addView(iv); } b = new Button(this); b.setText(getString(mNextButtons[mLayoutId])); b.setId(R.id.survival_next); ll.addView(b); sv.addView(ll); setContentView(sv); // Title Util.setTitleFont(this, findViewById(R.id.title)); // "Next" button final View nextButton = findViewById(R.id.survival_next); Util.setFont(this, nextButton, "Lobster.ttf"); nextButton.setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { final Intent next = new Intent(SurvivalActivity.this, SurvivalActivity.class); next.putExtra(KEY_LAYOUT_ID, (mLayoutId + 1) % mSurvivalLayouts.length); startActivity(next); finish(); } }); } 

As you can see, I am simply inflating the views directly, and Li (which is the alias of Log.i(TAG, ...) ) indicates what was the last ImageView inflated before any failure.

I launched the application, saw the 1st activity, then the second, and, as before, it crashes when the third one loads. The last identifier I saw in logcat was the last image of R.drawable.surv_8 . This is a file with a diagonal of 630x377, 45 kiB.

I tried to change the viewing order as follows:

 private static final int[][] mImageViews = { { R.drawable.surv_pioche, R.drawable.surv_crafting, R.drawable.surv_hache, R.drawable.surv_charbon, R.drawable.surv_torch, R.drawable.surv_abri }, { R.drawable.surv_malle, R.drawable.surv_four, R.drawable.surv_pelle, R.drawable.surv_repere, R.drawable.surv_sand, R.drawable.surv_glass }, { R.drawable.surv_8, R.drawable.surv_1, R.drawable.surv_2, R.drawable.surv_3, R.drawable.surv_4, R.drawable.surv_5, R.drawable.surv_6, R.drawable.surv_7, }, }; 

So, to go forward_8. The app still crashes in the last snapshot, which is R.drawable.surv_7 . So this is not a problem with the image.

Then I deleted one of the images from mImageViews[2] so that I had only 7 images instead of 8. The application works as expected: the activity loads, when I click the button, I go to the next one, and I go to the first one. Without crashing.

However, I can say that actions 1 and 2 remain in memory. Indeed, look at the timings:

 03-04 15:47:13.685 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +2s570ms 03-04 15:47:36.834 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +803ms 03-04 15:47:39.756 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +1s476ms 03-04 15:47:44.201 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +462ms 03-04 15:47:46.717 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +474ms 03-04 15:47:48.599 I/ActivityManager(17430): Displayed net.bicou.myapp/.SurvivalActivity: +474ms 

400 ms instead of 1.5-2.5 s to load activity.

Why does this remain in my memory?

=> ** Do I need to manually take all the pictures with onDestroy() ? **

=> What do you think about this: http://androidactivity.wordpress.com/2011/09/24/solution-for-outofmemoryerror-bitmap-size-exceeds-vm-budget/
With this solution, could I upload as many as 10 bitmaps around the world, and would I release them when I load another activity?

+3
source share
3 answers

I am sorry that the answer to this question was actually not possible with the elements that I gave in my question.

In fact, as mentioned in another question here: Why have all my bitmaps grown by 200%? , I put my photos in a drawable folder, which, on an xhdpi screen such as mine, will cause Android to pop up everything twice (to match xhdpi vs mdpi) and will use 400% of the memory (twice the width and twice 4x more pixels).

One more thing, since I do not want the user to be able to return (see finish() immediately after startActivity() ), this is what I entered in onPause() :

 private void unbindDrawables(View view) { if (view.getBackground() != null) { view.getBackground().setCallback(null); } if (view instanceof ViewGroup) { for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { unbindDrawables(((ViewGroup) view).getChildAt(i)); } ((ViewGroup) view).removeAllViews(); } } 

This is the code I got here in StackOverflow (for example, here: Is rice and a one-time bitmap better with memory? ).

Here are my comments on this code and why each line is important:

  • setCallback(null) removes the link to the activity, so it could be garbage collection. If you do not, all representations will be remembered.
  • You also need to call it from onPause , because onDestroy cannot be called.
  • After this, you may need to collect garbage to help the GC understand that there are some references to objects that were made by orphans, and which may be GC'd. Just call System.gc() .

This is the resulting onPause code, the main layout of which has the identifier top_layout :

 @Override protected void onPause() { super.onPause(); unbindDrawables(findViewById(R.id.top_layout)); System.gc(); } 
+5
source

Perhaps just the bitmap that you put in the ImageView is very large. Here are some things you can try:

Give up the xml layout and just inflate and show the problematic ImageViews or ImageViews and see if this error happens.

Also check each image to see what size they actually have. The size that they will take in memory is about 4 * width * height (in bytes, where width and height is the size of the image in pixels).

Since your Bitmap instances take up memory on the heap, I assume you are using Android 3 or higher. In these versions, where are the Bitmap instances (like any other Java instance). In Android 2.x and below, Bitmap instances were allocated to non-exotic memory. Knowing this, make sure that the high use of the heap that you get is really related to the images, and not to some other instances that are created a lot. Try debugging your constructors and see who causes what when the application starts / starts.

According to Bicou’s observations, you can manually upload images via BitmapFactory, and their number can be reduced (through the “Parameters” object). Also, try to keep a counter of how many bytes of the image in memory you are adding, increasing with a width of 4 * the width of each image that you actually load into the Bitmap object.

+2
source

The best way to optimize memory usage with bitmap images in your case is to keep a list of your ImageViews, do this until the call () is complete for each ImageView:

 ((BitmapDrawable) myImageView.getDrawable()).getBitmap().recycle(); myImageView = null; 
+1
source

Source: https://habr.com/ru/post/910295/


All Articles