1

I am trying to load some very small images (average size is 90kb) into a gridview in Android. Whenever I load more than 9 images then I am getting memory issues. I have tried scaling the images to a smaller size and although this works to a certain extent it is not really a true solution as the picture quality is awful.

The code is below

private Context mContext;
private ArrayList<Bitmap> photos = new ArrayList<Bitmap>();
public Bitmap [] mThumbIds;

public ImageAdapter(Context c) {
    mContext = c;
}


public Object getItem(int position) {
    return null;
}

public long getItemId(int position) {
    return 0;
}

public Bitmap scaleBitmap(String imagePath) {
    Bitmap resizedBitmap = null;
    try {
        int inWidth = 0;
        int inHeight = 0;

        InputStream in;

        in = new FileInputStream(imagePath);

        // decode image size (decode metadata only, not the whole image)
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(in, null, options);
        in.close();
        in = null;

        // save width and height
        inWidth = options.outWidth;
        inHeight = options.outHeight;

        // decode full image pre-resized
        in = new FileInputStream(imagePath);
        options = new BitmapFactory.Options();
        // calc rought re-size (this is no exact resize)
        options.inSampleSize = Math.max(inWidth/300, inHeight/300);
        // decode full image
        Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options);

        // calc exact destination size
        Matrix m = new Matrix();
        RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
        RectF outRect = new RectF(0, 0, 300, 300);
        m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
        float[] values = new float[9];
        m.getValues(values);

        // resize bitmap
        resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return resizedBitmap;
}


public void populateGrid() {
    File sdDir = new File("mnt/sdcard/Pictures");
    File[] sdDirFiles = sdDir.listFiles();
    for(File singleFile : sdDirFiles) {
        String filePath = singleFile.getAbsolutePath();
        Bitmap bmp = scaleBitmap(filePath);
        photos.add(bmp);
    }
    mThumbIds = photos.toArray(new Bitmap[(photos.size())]);
}

// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
    ImageView imageView;
    if (convertView == null) {  // if it's not recycled, initialize some attributes
        imageView = new ImageView(mContext);
        imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        imageView.setPadding(8, 8, 8, 8);
    } else {
        imageView = (ImageView) convertView;
    }
    imageView.setImageBitmap(mThumbIds[position]);
    return imageView;
}

@Override
public int getCount() {
    return mThumbIds.length;
}
}

2 Answers 2

2

Two considerations:

  1. The 90kb compressed image size doesn't really matter. The memory use is dictated by the actual resolution of the bitmap -- in this case, 300*300*4bpp, so about 360k per bitmap.

  2. Gingerbread has some flaws because of the combination of the fact that Bitmap memory is stored in a native array (rather than on the Java heap) combined with the fact that garbage collection occurs concurrently. Because of this fact, it sometimes takes the memory manager longer to realize that Bitmap memory can be re-used.

So, the ramifications of this are:

  1. Consider the actual decompressed Bitmap size when estimating memory usage.
  2. Recycle intermediate bitmaps as much as possible, to help get the memory reclaimed faster. For example, if you are scaling a bitmap, save a reference to the pre-scaled source, and recycle the source after scaling is complete (compare to the result of the scaling, since it's possible that the same Bitmap is returned).

If you can, test your code on an ICS device. You can then use the heap inspection tools to get a sense about where the most memory is being used. Sine ICS allocates bitmap memory on the Java heap, you'll have an accurate picture of the bitmap memory usage.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks very much for your response. I have tried recycling the bitmaps but keep getting an exception saying that I'm trying to used a recycled bitmap. Could you please recommend with an example where I should be recycling the bitmaps.
Having to recycle bitmaps is tricky. Make sure that you only recycle instances that are not still connected to a View. Are you experiencing these issues on Gingerbread? If it's not Ginberbread, then my suggestion about recycling is less likely to help, and it's more likely that you're simply leaking the Bitmaps somehow. Leaked Contexts are often the culprit here. If that is the case, using the heap tracing tool will help you to determine if Contexts are being kept alive for too long. The fact that you're holding an ArrayList of Bitmaps means there's a good chance that is the issue.
1

Bitmap use a lot of memory, if you have the image saved into memory already it would probably be better to just use the path of the file and push it on an ImaveView.

ImageView img = new ImageView(getApplicationContext());;

img.setImageBitmap(BitmapFactory.decodeFile(media.getThumbPath()));
img.setScaleType(ImageView.ScaleType.CENTER_INSIDE);

myLinearLayout.addView(img);

Something like this might work better. this way you are not storing all the Bitmaps into your heap.

2 Comments

Thanks for your response. I actually cannot do that as the bitmaps are created and read dynamically from the SD card so they cannot be stored as drawables.
Also, this comment is incorrect. At some point, the ImageView also has to decompress the image and display it, so the same amount of memory will be used in the end.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.