In my app I am using a listview and a listadapter. When one clicks on a certain subitem in the listview there's a clickable textview that loads several bitmaps to a scrollview - and this scrollview is shown in an alertdialog.
All this happens in a class that extends BaseExpandableListAdapter and when this textlink is clicked - a static inner class is called that is responsible to load all these (9) bitmaps. This inner class extends asynctask.
Before these bitmaps are loaded to the scrollview - two static methods of this inner-class are called that scaleddown the bitamps to a size that fits the screen. Here I use Bitmapfactory.decoderesource and Bitmap.scaledownBitmap.
All this works FINE, .. BUT the program suffers from a memoryleak. This leak was rather big before because this inner class was non-static. So the leak was reduced by making this inner class static. Yes - reduced, but not eliminated.
I have also made weakreference of several objects but without success. For example - I made a weak reference of the object that refers to the inner-class. I have made a weak reference of the context that is passed to the innerclass. I have even made w weak reference of the bitmaps. No success at all.
The heapsize of my Samsung Galazy s3 is 64 MB. When the listview with all its subitem is first loaded used heap is about 17 MB. Then when 9 bitmaps are loaded it jumbs to about 42 MB. If I then click on another subitem with images used heap is the same - but after continue clicking and loading bitmaps the heap suddenly jumbs to 47 MB ... then the same scenario .... stands still for a while - then up to 52 MB .... 56 MB. So I have to click and load bitmaps pretty much to get out-of-memory. Let say 15 - 20 minutes of intensive use.
conclusion: To make the inner-class static helped me to reduce the memory leak. But despite making weakreferences of several objects (especially context) I have not been able to reduce leak further.
Any suggestions?
The code below is a bit messy ....
static class BitmapWorkerTask extends AsyncTask <Integer, Void, Bitmap[]> {
private int[] data;
private int[] width, height;
private int nmbrOfImages;
private String[] scrollText;
private ImageView mImage;
private View view;
private LayoutInflater factory;
private AlertDialog.Builder alertadd;
private Context context;
private WeakReference <Context> sc;
private WeakReference <ImageView> mImageV;
private WeakReference <Bitmap[]> bitmapV;
public BitmapWorkerTask(int[] width, int[] height, int nmbrOfImages, String[] scrollText, Context context) {
this.width = width;
this.height = height;
this.nmbrOfImages = nmbrOfImages;
this.scrollText = scrollText;
this.context = context;
mImage = null;
view = null;
factory = null;
alertadd = null;
System.gc();
sc = new WeakReference <Context> (context);
try {
for (int i = 0; i < scaledBitmap.length; i++) {
scaledBitmap[i].recycle();
scaledBitmap[i] = null;
}
} catch (NullPointerException ne) {
System.out.println("nullpointerexception ... gick inte recycla bitmapbilder");
}
switch (nmbrOfImages) {
case 0:
data = new int[1];
break;
case 1:
data = new int[3];
break;
case 2:
data = new int[5];
break;
case 3:
data = new int[9];
break;
}
}
@Override
protected Bitmap[] doInBackground(Integer ... params) {
switch (nmbrOfImages) {
case 0:
data[0] = params[0];
break;
case 1:
data[0] = params[0];
data[1] = params[1];
data[2] = params[2];
break;
case 2:
data[0] = params[0];
data[1] = params[1];
data[2] = params[2];
data[3] = params[3];
data[4] = params[4];
break;
case 3:
data[0] = params[0];
data[1] = params[1];
data[2] = params[2];
data[3] = params[3];
data[4] = params[4];
data[5] = params[5];
data[6] = params[6];
data[7] = params[7];
data[8] = params[8];
break;
}
alertadd = new AlertDialog.Builder(sc.get());
factory = LayoutInflater.from(sc.get());
Bitmap[] bm = decodeSampledBitmapFromResource(sc.get().getResources(), data, width, height);
bitmapV = new WeakReference <Bitmap[]> (bm);
return bitmapV.get();
}
protected void onPostExecute(Bitmap[] bitmap) {
switch (nmbrOfImages) {
case 0:
if (view == null) {
view = factory.inflate(R.layout.alertviews, null);
}
mImage = (ImageView) view.findViewById(R.id.extra_img);
mImage.setImageBitmap(bitmap[0]);
alertadd.setView(view);
alertadd.setNeutralButton("Here!", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dlg, int sumthin) {
}
});
alertadd.show();
break;
case 1:
if (view == null) {
view = factory.inflate(R.layout.alertviews2, null);
}
mImage = (ImageView) view.findViewById(R.id.img1);
mImage.setImageBitmap(bitmap[0]);
mImage = (ImageView) view.findViewById(R.id.img2);
mImage.setImageBitmap(bitmap[1]);
mImage = (ImageView) view.findViewById(R.id.img3);
mImage.setImageBitmap(bitmap[2]);
try {
TextView mText2 = (TextView) view.findViewById(R.id.text_img1_scrollview);
mText2.setText(scrollText[0]);
mText2 = (TextView) view.findViewById(R.id.text_img2_scrollview);
mText2.setText(scrollText[1]);
mText2 = (TextView) view.findViewById(R.id.text_img3_scrollview);
mText2.setText(scrollText[2]);
} catch (NullPointerException ne) {
System.out.println("nullpointerexception ... TextView i metoden onPostExecute");
}
alertadd.setView(view);
alertadd.setNeutralButton("Here!", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dlg, int sumthin) {
}
});
alertadd.show();
break;
case 2:
if (view == null) {
view = factory.inflate(R.layout.alertviews3, null);
}
mImage = (ImageView) view.findViewById(R.id.img1);
mImage.setImageBitmap(bitmap[0]);
mImage = (ImageView) view.findViewById(R.id.img2);
mImage.setImageBitmap(bitmap[1]);
mImage = (ImageView) view.findViewById(R.id.img3);
mImage.setImageBitmap(bitmap[2]);
mImage = (ImageView) view.findViewById(R.id.img4);
mImage.setImageBitmap(bitmap[3]);
mImage = (ImageView) view.findViewById(R.id.img5);
mImage.setImageBitmap(bitmap[4]);
try {
TextView mText3 = (TextView) view.findViewById(R.id.text_img1_scrollview);
mText3.setText(scrollText[0]);
mText3 = (TextView) view.findViewById(R.id.text_img2_scrollview);
mText3.setText(scrollText[1]);
mText3 = (TextView) view.findViewById(R.id.text_img3_scrollview);
mText3.setText(scrollText[2]);
mText3 = (TextView) view.findViewById(R.id.text_img4_scrollview);
mText3.setText(scrollText[3]);
mText3 = (TextView) view.findViewById(R.id.text_img5_scrollview);
mText3.setText(scrollText[4]);
} catch (NullPointerException ne) {
System.out.println("nullpointerexception ... TextView i metoden onPostExecute");
}
alertadd.setView(view);
alertadd.setNeutralButton("Here!", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dlg, int sumthin) {
}
});
alertadd.show();
break;
case 3:
if (view == null) {
view = factory.inflate(R.layout.alertviews4, null);
}
AlertDialog.Builder alertadd = new AlertDialog.Builder(context);
mImage = (ImageView) view.findViewById(R.id.img1);
mImage.setImageBitmap(bitmap[0]);
mImage = (ImageView) view.findViewById(R.id.img2);
mImage.setImageBitmap(bitmap[1]);
mImage = (ImageView) view.findViewById(R.id.img3);
mImage.setImageBitmap(bitmap[2]);
mImage = (ImageView) view.findViewById(R.id.img4);
mImage.setImageBitmap(bitmap[3]);
mImage = (ImageView) view.findViewById(R.id.img5);
mImage.setImageBitmap(bitmap[4]);
mImage = (ImageView) view.findViewById(R.id.img6);
mImage.setImageBitmap(bitmap[5]);
mImage = (ImageView) view.findViewById(R.id.img7);
mImage.setImageBitmap(bitmap[6]);
mImage = (ImageView) view.findViewById(R.id.img8);
mImage.setImageBitmap(bitmap[7]);
mImage = (ImageView) view.findViewById(R.id.img9);
mImage.setImageBitmap(bitmap[8]);
try {
TextView mText4 = (TextView) view.findViewById(R.id.text_img1_scrollview);
mText4.setText(scrollText[0]);
mText4 = (TextView) view.findViewById(R.id.text_img2_scrollview);
mText4.setText(scrollText[1]);
mText4 = (TextView) view.findViewById(R.id.text_img3_scrollview);
mText4.setText(scrollText[2]);
mText4 = (TextView) view.findViewById(R.id.text_img4_scrollview);
mText4.setText(scrollText[3]);
mText4 = (TextView) view.findViewById(R.id.text_img5_scrollview);
mText4.setText(scrollText[4]);
mText4 = (TextView) view.findViewById(R.id.text_img6_scrollview);
mText4.setText(scrollText[5]);
mText4 = (TextView) view.findViewById(R.id.text_img7_scrollview);
mText4.setText(scrollText[6]);
mText4 = (TextView) view.findViewById(R.id.text_img8_scrollview);
mText4.setText(scrollText[7]);
mText4 = (TextView) view.findViewById(R.id.text_img9_scrollview);
mText4.setText(scrollText[8]);
} catch (NullPointerException ne) {
System.out.println("nullpointerexception ... TextView i metoden onPostExecute");
}
alertadd.setView(view);
alertadd.setNeutralButton("Here!", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dlg, int sumthin) {
}
});
alertadd.show();
break;
}
}
/**
*
* @param options
* @param reqW
* @param reqH
* @return
*/
public static int calculateInSampleSize(BitmapFactory.Options options, int reqW, int reqH) {
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
int inSampleSize = 1;
if (imageHeight > reqH || imageWidth > reqW) {
int heightRatio = Math.round((float) imageHeight / (float) reqH);
int widthRatio = Math.round((float) imageWidth / (float) reqW);
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
System.out.println("i if-satsen!");
System.out.println("height-ratio: " + heightRatio + "\nwidth-ratio: " + widthRatio);
}
System.out.println("samplesize: " + inSampleSize);
inSampleSize = inSampleSize;
return inSampleSize;
}
@SuppressLint("NewApi")
/**
*
* @param res
* @param resId
* @param reqW
* @param reqH
* @return
*/
public static Bitmap[] decodeSampledBitmapFromResource(Resources res, int[] resId, int[] reqW, int[] reqH) {
scaledBitmap = new Bitmap[resId.length];
BitmapFactory.Options options;
for (int i = 0; i < resId.length; i++) {
options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bm =BitmapFactory.decodeResource(res, resId[i], options);
System.out.println("ursprunglig bild: h = " + options.outHeight + " w = " + options.outWidth);
options.inSampleSize = calculateInSampleSize(options, reqW[i], reqH[i]);
while (options.outHeight < reqH[i] || options.outWidth < reqW[i]) {
options.inSampleSize--;
System.out.println("räknar nu ner insampleseize\ninSamleSize =" + options.inSampleSize);
}
options.inJustDecodeBounds = false;
bm = BitmapFactory.decodeResource(res, resId[i], options);
System.out.println("innan omskalning: h = " + options.outHeight + " w = " + options.outWidth);
System.out.println("antalet bytes: " + bm.getByteCount());
System.out.println("native free size: " + Debug.getNativeHeapFreeSize() );
scaledBitmap[i] = Bitmap.createScaledBitmap(bm, reqW[i], reqH[i], true);
bm.recycle();
bm = null;
}
System.gc();
WeakReference <Bitmap[] > sc = new WeakReference <Bitmap[]> (scaledBitmap);
return sc.get();
}
}
}
You are keeping dual reference to your bitmaps once weak and once hard copy your problem code is :
Bitmap[] bm = decodeSampledBitmapFromResource(sc.get().getResources(), data, width, height);
bitmapV = new WeakReference <Bitmap[]> (bm);
you are not getting rid of the contents of bm
and you are doing it more than once
scaledBitmap[i] = Bitmap.createScaledBitmap(bm, reqW[i], reqH[i], true);
bm.recycle();
bm = null;
}
System.gc();
WeakReference <Bitmap[] > sc = new WeakReference <Bitmap[]> (scaledBitmap);
You should do this:
ArrayList<WeakReference<Bitmap>>scaledBitmaps= new ArrayList<WeakReference<Bitmap>>();
scaledBitmaps.add(new WeakReference<Bitmap>(Bitmap.createScaledBitmap...));
and do not use Bitmap[] array
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