Android bitmap imageview memory leak
To reduce memory, you can try out these things :
- After converting drawables into bitmaps, you can set the imageResourceMap to null. This will unload the 3 drawables.
- Avoid storing a reference to the imageViews. You might be storing imageViews even after they are removed from the UI
- Recycle the bitmaps more often. Instead of just onDestroy, as soon as you know that one bitmap is not used, you can recycle it.
Edit : based on the conversation in the comments : The bitmap returned by BitmapFactory.decodeStream(fis, null, o) is not assigned to any variable and hence is not recycled. Android 2.2 and 2.3 will have leaks in this line.
Dénes
Updated on June 08, 2022Comments
-
Dénes almost 2 years
I put 4x4 imageView to an activity(BoardActivity), and user can change the images by clicking them. With HTC Desire (Android 2.2.2), I got OOM(Out Of Memory) in about 30 minutes of intensive useage -EDIT: 16th start of this activity-, but no other devices produces this (android 2.1, and android 2.2.1). Is it possible, that I made some mistake with the bitmap/imageview useage and that causes this error? First, I load all resource ID into a map:
private Map<String, Integer> imageResourceMap; imageResourceMap = new HashMap<String, Integer>(); imageResourceMap.put("a", R.drawable.a); imageResourceMap.put("b", R.drawable.b); imageResourceMap.put("c", R.drawable.c); //... I store 55 drawable's resourceId in this map
Then I resize and save every image into bitmap, represented in Map:
private static Map<String, Bitmap> imageBitmap; private void loadBitmaps(int imagesSize) { for (String s : imageResourceMap.keySet()) { Bitmap tmp = getBitmapFromRes(imageResourceMap.get(s), imagesSize); imageBitmap.put(s, tmp); } } private Bitmap getBitmapFromRes(int resId, int imageSize) { Bitmap b = null; try { // Decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; InputStream fis = getResources().openRawResource(resId); BitmapFactory.decodeStream(fis, null, o); fis.close(); int scale = 1; if (o.outHeight > imageSize || o.outWidth > imageSize) { scale = (int) Math.pow(2, (int) Math.round(Math.log(imageSize / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5))); } // Decode with inSampleSize BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize = scale; fis = getResources().openRawResource(resId); b = BitmapFactory.decodeStream(fis, null, o2); fis.close(); } catch (IOException e) { e.printStackTrace(); } return b; }
I keep the imageViews in an array, and init all images with this function:
private static ImageView[][] imageViews; private ImageView getImage(String name) { MyImageView item = new MyImageView(this, i, j, c + ""); item.setImageBitmap(imageBitmap.get(name)); item.setAdjustViewBounds(true); return item; }
When I need to change an image, I simple change its resource:
imageViews[i][j].setImageBitmap(imageBitmap.get("a"));
And right before I finish the activity, I recycle the bitmap map:
private void recycleImageBitmaps() { for (Bitmap b : imageBitmap.values()) { if (b != null) { b.recycle(); } } }
In AndroidManifest.xml I declared this activity "singleTask":
<activity android:name=".game.board.BoardActivity" android:launchMode="singleTask"> </activity>
In this application(game), we reopen this activity a several times... What did I wrong? Can this cause the Out Of Memory error?
CORRECTION Corrected the getBitmapFromRes like this:
private Bitmap getBitmapFromRes(int resId, int imageSize) { Bitmap tmp = null; Bitmap b = null; try { // Decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; InputStream fis = getResources().openRawResource(resId); tmp = BitmapFactory.decodeStream(fis, null, o); fis.close(); int scale = 1; if (o.outHeight > imageSize || o.outWidth > imageSize) { scale = (int) Math.pow(2, (int) Math.round(Math.log(imageSize / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5))); } // Decode with inSampleSize BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize = scale; fis = getResources().openRawResource(resId); b = BitmapFactory.decodeStream(fis, null, o2); fis.close(); } catch (IOException e) { e.printStackTrace(); }finally{ if(tmp != null){ tmp.recycle(); tmp = null; } } return b; }
HTC still crashed at the 11th start of this activity.
EDIT: This activity(BoardActivity) launch from an Activity(MenuActivity), which have 4 imageButton, and is in a Tabhost activity. The imageButtons declarations look like this:
<ImageButton android:id="@+id/game_menu_CreateButton" android:layout_width="120dip" android:layout_height="120dip" android:layout_alignRight="@+id/textView1" android:layout_alignTop="@+id/textView1" android:background="@drawable/create" android:layout_marginRight="1sp" android:layout_marginTop="10sp" />
When I start the BoardActivity from MenuActivity, I don't call
finish()
at MenuActivity, and when I callfinish()
at the BoardActivity, I don't start a new intent, so it just return to the already opened MenuActivity. And the 16 round of this, I got the OOM.