Android bitmap imageview memory leak
android, bitmap, imageview, memory-leaks, out-of-memory
Solution
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.
Problem
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 call `finish()` 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.