When I minimize my Android App for about 4 or 5 times, I always get the following error:
02-01 19:24:11.980: E/dalvikvm-heap(22362): Out of memory on a 3686416-byte allocation.
02-01 19:24:12.000: E/dalvikvm(22362): Out of memory: Heap Size=62755KB, Allocated=55237KB, Limit=65536KB
02-01 19:24:12.000: E/dalvikvm(22362): Extra info: Footprint=62435KB, Allowed Footprint=62755KB, Trimmed=2144KB
02-01 19:24:12.000: E/Bitmap_JNI(22362): Create Bitmap Failed.
02-01 19:24:12.000: E/Bitmap_JNI(22362): Failed to create SkBitmap!
02-01 19:24:12.000: E/AndroidRuntime(22362): FATAL EXCEPTION: main
02-01 19:24:12.000: E/AndroidRuntime(22362): java.lang.OutOfMemoryError: (Heap Size=62755KB, Allocated=55237KB)
02-01 19:24:12.000: E/AndroidRuntime(22362): at android.graphics.Bitmap.nativeCreateScaledBitmap(Native Method)
02-01 19:24:12.000: E/AndroidRuntime(22362): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:744)
02-01 19:24:12.000: E/AndroidRuntime(22362): at de.vauge.mb.Utils.getResizedBitmap(Utils.java:56)
02-01 19:24:12.000: E/AndroidRuntime(22362): at de.vauge.mb.MenuView.initialize(MenuView.java:74)
02-01 19:24:12.000: E/AndroidRuntime(22362): at de.vauge.mb.MenuView$1.handleMessage(MenuView.java:137)
02-01 19:24:12.000: E/AndroidRuntime(22362): at android.os.Handler.dispatchMessage(Handler.java:99)
02-01 19:24:12.000: E/AndroidRuntime(22362): at android.os.Looper.loop(Looper.java:156)
02-01 19:24:12.000: E/AndroidRuntime(22362): at android.app.ActivityThread.main(ActivityThread.java:5045)
02-01 19:24:12.000: E/AndroidRuntime(22362): at java.lang.reflect.Method.invokeNative(Native Method)
02-01 19:24:12.000: E/AndroidRuntime(22362): at java.lang.reflect.Method.invoke(Method.java:511)
02-01 19:24:12.000: E/AndroidRuntime(22362): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
02-01 19:24:12.000: E/AndroidRuntime(22362): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
02-01 19:24:12.000: E/AndroidRuntime(22362): at dalvik.system.NativeStart.main(Native Method)
My App consists of only one Activity which has 7 different, self-written Views (all of them containing some Bitmaps) in it, and they are toggled invisible when they are not needed (Probably not good style, but it worked for me until now...). Every one of those Views has a destroy()-function that recycles all the Bitmaps that are used in it, and the onDestroy() of the MainActivity calls all those destroy()-functions. Furthermore, I did not use any static Bitmaps.
So, is there anything else I could try in addition to recycling all Bitmaps and not using static Bitmaps?
Well. Bitmaps on Android can be a little tricky. Can you give better information about the sources of the bitmaps and their sizes?
Otherwise, I'd recommend looking into these things:
If you're loading remote images, check out fresco. You can also check out Picasso. I personally used to like ImageLoader, but it is not being maintained anymore.
If you're using the inPurgable flag that used to be a recommended option, try to find a way around it as it actually causes more memory to be allocated for each image.
If you decode small, local assets frequently, consider saving your drawables in a hashmap and reusing them when needed. Less GC.
If you care to subclass your Application, you can use the OnLowMemory call to know when you probably really need to clean up (mostly good for debugging, not real life situations) ... If that's not too late... :)
Take a look at Chris Banes' blog. This is a pretty interesting memory cache solution
Implement a memory trimmer you call whenever needed and possible.
One other unsurprising optimization is to use smaller objects when you can... Think of your minimal data models and image sizes and try to have a conforming API for those.
For #3 from Ben Max comment I maked two useful classes:
public abstract class SoftReferenceStorage<K, V>{
private static HashMap<Object, SoftReference<Object>> objectsHash = new HashMap<Object, SoftReference<Object>>();
@SuppressWarnings("unchecked")
public V get(K key) {
if (objectsHash.containsKey(key)) {
SoftReference<Object> ref = objectsHash.get(key);
if (ref.get() == null) {
objectsHash.put(key, new SoftReference<Object>(createValueForKey(key)));
return (V)objectsHash.get(key).get();
} else {
return (V)ref.get();
}
} else {
objectsHash.put(key, new SoftReference<Object>(createValueForKey(key)));
return (V)objectsHash.get(key).get();
}
}
protected abstract V createValueForKey(K key);
}
and
public class FrequentlyUsedBitmapResources extends SoftReferenceStorage<Integer, Bitmap>{
private static FrequentlyUsedBitmapResources instance = null;
private Resources resources;
public FrequentlyUsedBitmapResources(Resources resources) {
super();
this.resources = resources;
}
public static FrequentlyUsedBitmapResources getInstance() {
if (instance == null) {
instance = new FrequentlyUsedBitmapResources(HiDriveApp.getContext().getResources());
}
return instance;
}
@Override
protected Bitmap createValueForKey(Integer resId) {
return BitmapFactory.decodeResource(resources, resId);
}
}
can be used like:
Bitmap b = FrequentlyUsedBitmapResources.getInstance().get(R.drawable.overview_photo_placeholder);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With