OutOfMemoryError creates raster images

As the title says, I exclude the exception from memory when loading layouts. One would think that this is a memory leak problem, but after the battle for 2 days I am no longer sure. The app has 10+ activities, most of which have background images.

A few facts / discoveries I made:

  • so far, the problem only appeared on the Galaxy Nexus running Android 4.0.3. I could not play it on Nexus S (4.1.1) and Galaxy S II (2.3.3).

  • screen orientation does not change. In fact, most of my actions were blocked in the portrait.

  • just for fun. I added a finish() call when opening a new action, so there would be no more than one action in memory at that time. Make sure that onDestroy is onDestroy .

defined as:

 @Override public void onDestroy() { super.onDestroy(); cleanupDrawables(contentView); // null all the fields referencing views and drawables System.gc(); } 

where cleanupDrawables() :

 protected static void cleanupDrawables(View view) { cleanupDrawable(view.getBackground()); if (view instanceof ImageView) cleanupDrawable(((ImageView)view).getDrawable()); else if (view instanceof TextView) { TextView tv = (TextView)view; Drawable[] compounds = tv.getCompoundDrawables(); for (int i = 0; i < compounds.length; i++) cleanupDrawable(compounds[i]); } else if (view instanceof ViewGroup && !(view instanceof AdapterView)) { ViewGroup vg = (ViewGroup)view; for (int i = 0; i < vg.getChildCount(); i++) cleanupDrawables(vg.getChildAt(i)); vg.removeAllViews(); } } protected static void cleanupDrawable(Drawable d) { if (d == null) return; d.setCallback(null); if (d instanceof BitmapDrawable) ((BitmapDrawable)d).getBitmap().recycle(); else if (d instanceof LayerDrawable) { LayerDrawable layers = (LayerDrawable)d; for (int i = 0; i < layers.getNumberOfLayers(); i++) cleanupDrawable(layers.getDrawable(i)); } } 
  • looking at the Eclipse heap inspector, the memory seems stable, i.e. some actions take up more memory than others, but it frees up after closing and looks stable over time.

  • according to the corresponding answers to the SO-images are stored in the native memory, but this dude claims that they should be on the heap with Android 3, so I should see an increase in memory if this is really an image memory loss.

The end result of my efforts is that I still go out of memory, although not as fast as before. Before an error occurs, I begin to see visible damage to the bitmap, which was not there before adding the cleanupDrawables() code. I deduced that calling Bitmap.recycle() causes corruption, although this code is only called onDestroy . Corruption appears on both bitmaps, which are parts of common styles that appear in many actions, as well as in bitmaps displayed in only one action.

In short, the results of my research are rather inconclusive. At the moment, I do not know what else to try.

Error stack trace for reference:

 08-22 10:49:51.889: E/AndroidRuntime(31697): java.lang.RuntimeException: Unable to start activity ComponentInfo{klick.beatbleeds/klick.beatbleeds.Bleeds}: android.view.InflateException: Binary XML file line #67: Error inflating class <unknown> 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1955) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1980) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.access$600(ActivityThread.java:122) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1146) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.os.Handler.dispatchMessage(Handler.java:99) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.os.Looper.loop(Looper.java:137) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.main(ActivityThread.java:4340) 08-22 10:49:51.889: E/AndroidRuntime(31697): at java.lang.reflect.Method.invokeNative(Native Method) 08-22 10:49:51.889: E/AndroidRuntime(31697): at java.lang.reflect.Method.invoke(Method.java:511) 08-22 10:49:51.889: E/AndroidRuntime(31697): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784) 08-22 10:49:51.889: E/AndroidRuntime(31697): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551) 08-22 10:49:51.889: E/AndroidRuntime(31697): at dalvik.system.NativeStart.main(Native Method) 08-22 10:49:51.889: E/AndroidRuntime(31697): Caused by: android.view.InflateException: Binary XML file line #67: Error inflating class <unknown> 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.createView(LayoutInflater.java:606) 08-22 10:49:51.889: E/AndroidRuntime(31697): at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.onCreateView(LayoutInflater.java:653) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:678) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.rInflate(LayoutInflater.java:739) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.inflate(LayoutInflater.java:489) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.inflate(LayoutInflater.java:396) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.inflate(LayoutInflater.java:352) 08-22 10:49:51.889: E/AndroidRuntime(31697): at klick.beatbleeds.ActivityBase.setContentView(ActivityBase.java:82) 08-22 10:49:51.889: E/AndroidRuntime(31697): at klick.beatbleeds.Bleeds.onCreate(Bleeds.java:40) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.Activity.performCreate(Activity.java:4465) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1919) 08-22 10:49:51.889: E/AndroidRuntime(31697): ... 11 more 08-22 10:49:51.889: E/AndroidRuntime(31697): Caused by: java.lang.reflect.InvocationTargetException 08-22 10:49:51.889: E/AndroidRuntime(31697): at java.lang.reflect.Constructor.constructNative(Native Method) 08-22 10:49:51.889: E/AndroidRuntime(31697): at java.lang.reflect.Constructor.newInstance(Constructor.java:417) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.LayoutInflater.createView(LayoutInflater.java:586) 08-22 10:49:51.889: E/AndroidRuntime(31697): ... 23 more 08-22 10:49:51.889: E/AndroidRuntime(31697): Caused by: java.lang.OutOfMemoryError 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.Bitmap.nativeCreate(Native Method) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.Bitmap.createBitmap(Bitmap.java:605) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.Bitmap.createBitmap(Bitmap.java:551) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:524) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:499) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:773) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.content.res.Resources.loadDrawable(Resources.java:1937) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.content.res.TypedArray.getDrawable(TypedArray.java:601) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.View.<init>(View.java:2780) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.view.ViewGroup.<init>(ViewGroup.java:385) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.widget.LinearLayout.<init>(LinearLayout.java:174) 08-22 10:49:51.889: E/AndroidRuntime(31697): at android.widget.LinearLayout.<init>(LinearLayout.java:170) 08-22 10:49:51.889: E/AndroidRuntime(31697): ... 26 more 

Here is a screenshot of one of the views to understand how many images are used here enter image description here

+6
source share
1 answer

Use these static methods to find out the exact problem (s):

 public static void showBitmapSize(Bitmap bitmap) { Log.d("test", "bitmap dimensions: w:" + bitmap.getWidth() + ", h:" + bitmap.getHeight() + " memory: " + (bitmap.getRowBytes() * bitmap.getHeight() / 1048576d)); } 

And the most important thing:

 static double lastavail; static double initavail; static boolean first = true; public static void showMemoryStats() { showMemoryStats(""); } public static void showMemoryStats(String message) { Log.i("memory", message + "----------------------------------------------------------------------------------------"); double nativeUsage = Debug.getNativeHeapAllocatedSize(); Log.i("memory", "nativeUsage: " + (nativeUsage / 1048576d)); //current heap size double heapSize = Runtime.getRuntime().totalMemory(); // Log.i("memory", "heapSize: " + (heapSize / 1048576d)); //amount available in heap double heapRemaining = Runtime.getRuntime().freeMemory(); // Log.i("memory", "heapRemaining: " + (heapRemaining / 1048576d)); double memoryAvailable = Runtime.getRuntime().maxMemory() - (heapSize - heapRemaining) - nativeUsage; Log.i("memory", "memoryAvailable: " + (memoryAvailable / 1048576d)); if (first) { initavail = memoryAvailable; first = false; } if (lastavail > 0) { Log.i("memory", "consumed since last: " + ((lastavail - memoryAvailable) / 1048576d)); } Log.i("memory", "consumed total: " + ((initavail - memoryAvailable) / 1048576d)); lastavail = memoryAvailable; Log.i("memory", "-----------------------------------------------------------------------------------------------"); } 

Dividing by 1048576 is only getting values ​​in MB (at least for me it is easier to think in MB).

Place a call to showMemoryStats with some meaningful message before calling setContentView() , and the other after it. And when you start a new activity, etc. In the end, you will find the exact causes of your problem.

Manual recirculation may be required. I had to implement it in some places of my application. In addition, using an intensive raster image (many backgrounds and pictures), such problems arose on all devices. Using these methods, I was able to find all the problems and deal with them.

Oh. And there is a possible quick fix for your problem, you say that it appears only in Galaxy Nexus. This is the only xhdpi device you mentioned. You probably have all the bitmaps in the drawable or drawable-hdpi folder only. The xhdpi device will accept bitmaps from drawable or drawable-hdpi and scale (although they may already be in the correct size), and this will consume a lot of memory. Decision. Create a drawable-xhdpi folder if it does not exist and place copies of the bitmap images there.

+2
source

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


All Articles