Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android OutOfMemoryError: bitmap size exceeds VM budget while loading images [duplicate]

Possible Duplicate:
Android: Strange out of memory issue while loading an image to a Bitmap object

I am using an Image Loader which loads the image from the URL and caches in into my file system. I am finding the following crash reports from few phones using ACRA

Error:

java.lang.OutOfMemoryError: bitmap size exceeds VM budget
    at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:493)
    at com.m7.nomad.utils.ImageLoader.decodeFile(ImageLoader.java:117)
    at com.m7.nomad.utils.ImageLoader.getBitmap(ImageLoader.java:88)
    at com.m7.nomad.utils.ImageLoader.access$0(ImageLoader.java:68)
    at com.m7.nomad.utils.ImageLoader$PhotosLoader.run(ImageLoader.java:170)

ImageLoader.java

package com.m7.nomad.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.Stack;
import java.util.WeakHashMap;

import com.m7.nomad.R;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageView;

public class ImageLoader {

    MemoryCache memoryCache = new MemoryCache();
    FileCache fileCache;
    private Map<ImageView, String> imageViews = Collections
            .synchronizedMap(new WeakHashMap<ImageView, String>());

    public ImageLoader(Context context) {
        // Make the background thread low priority because it will not affect
        // the
        // UI performance
        photoLoaderThread.setPriority(Thread.NORM_PRIORITY - 1);

        fileCache = new FileCache(context);
    }

    final int stub_id = R.drawable.image_place_holder;

    public void DisplayImage(String url, Activity activity, ImageView imageView) {
        imageViews.put(imageView, url);
        Bitmap bitmap = memoryCache.get(url);
        if (bitmap != null)
            imageView.setImageBitmap(bitmap);
        else {
            queuePhoto(url, activity, imageView);
            imageView.setImageResource(stub_id);
        }
    }

    private void queuePhoto(String url, Activity activity, ImageView imageView) {
        // This ImageView may be used for other images before. So there may be
        // some old tasks in the queue. We need to discard them.
        photosQueue.Clean(imageView);
        PhotoToLoad p = new PhotoToLoad(url, imageView);
        synchronized (photosQueue.photosToLoad) {
            photosQueue.photosToLoad.push(p);
            photosQueue.photosToLoad.notifyAll();
        }

        // start thread if it's not started yet
        if (photoLoaderThread.getState() == Thread.State.NEW)
            photoLoaderThread.start();
    }

    private Bitmap getBitmap(String url) {
        File f = fileCache.getFile(url);

        // from SD cache
        Bitmap b = decodeFile(f);
        if (b != null)
            return b;

        // from web
        try {
            Bitmap bitmap = null;
            URL imageUrl = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) imageUrl
                    .openConnection();
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(30000);
            InputStream is = conn.getInputStream();
            OutputStream os = new FileOutputStream(f);
            Utils.CopyStream(is, os);
            os.close();
            bitmap = decodeFile(f);
            return bitmap;
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    // decodes image and scales it to reduce memory consumption
    private Bitmap decodeFile(File f) {
        try {
            // decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(f), null, o);

            // Find the correct scale value. It should be the power of 2.
            final int REQUIRED_SIZE = 65;
            int width_tmp = o.outWidth, height_tmp = o.outHeight;
            int scale = 1;
            /*
             * while (true) { if (width_tmp / 2 < REQUIRED_SIZE || height_tmp /
             * 2 < REQUIRED_SIZE) break; width_tmp /= 2; height_tmp /= 2; scale
             * *= 2; }
             */

            // decode with inSampleSize
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
        } catch (FileNotFoundException e) {
        }
        return null;
    }

    // Task for the queue
    private class PhotoToLoad {
        public String url;
        public ImageView imageView;

        public PhotoToLoad(String u, ImageView i) {
            url = u;
            imageView = i;
        }
    }

    PhotosQueue photosQueue = new PhotosQueue();

    public void stopThread() {
        photoLoaderThread.interrupt();
    }

    // stores list of photos to download
    class PhotosQueue {
        private Stack<PhotoToLoad> photosToLoad = new Stack<PhotoToLoad>();

        // removes all instances of this ImageView
        public void Clean(ImageView image) {
            for (int j = 0; j < photosToLoad.size();) {
                if (photosToLoad.get(j).imageView == image)
                    photosToLoad.remove(j);
                else
                    ++j;
            }
        }
    }

    class PhotosLoader extends Thread {
        public void run() {
            try {
                while (true) {
                    // thread waits until there are any images to load in the
                    // queue
                    if (photosQueue.photosToLoad.size() == 0)
                        synchronized (photosQueue.photosToLoad) {
                            photosQueue.photosToLoad.wait();
                        }
                    if (photosQueue.photosToLoad.size() != 0) {
                        PhotoToLoad photoToLoad;
                        synchronized (photosQueue.photosToLoad) {
                            photoToLoad = photosQueue.photosToLoad.pop();
                        }
                        Bitmap bmp = getBitmap(photoToLoad.url);
                        memoryCache.put(photoToLoad.url, bmp);
                        String tag = imageViews.get(photoToLoad.imageView);
                        if (tag != null && tag.equals(photoToLoad.url)) {
                            BitmapDisplayer bd = new BitmapDisplayer(bmp,
                                    photoToLoad.imageView);
                            Activity a = (Activity) photoToLoad.imageView
                                    .getContext();
                            a.runOnUiThread(bd);
                        }
                    }
                    if (Thread.interrupted())
                        break;
                }
            } catch (InterruptedException e) {
                // allow thread to exit
            }
        }
    }

    PhotosLoader photoLoaderThread = new PhotosLoader();

    // Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable {
        Bitmap bitmap;
        ImageView imageView;

        public BitmapDisplayer(Bitmap b, ImageView i) {
            bitmap = b;
            imageView = i;
        }

        public void run() {
            if (bitmap != null)
                imageView.setImageBitmap(bitmap);
            else
                imageView.setImageResource(stub_id);
        }
    }

    public void clearCache() {
        memoryCache.clear();
        fileCache.clear();
    }

}

This is how i call the ImageLoader

ImageView image = new ImageView(getActivity());

        ImageLoader imgLoader = new ImageLoader(MainActivity.context);
        imgLoader.DisplayImage(imagePath, new Activity(), image);
like image 664
Harsha M V Avatar asked Jan 19 '13 09:01

Harsha M V


1 Answers

Your heap is growing unexpectedly That is due to Image size according to Android

1st scale down the image size loaded from url and then load it to your view (Activity) to scale down the Image you can use the following code snippet that will take image and dimenstion and will result the scaled down image

    public static Bitmap getResizedBitmap(Bitmap image, int newHeight, int newWidth) {
    int width = image.getWidth();
    int height = image.getHeight();
    float scaleWidth = ((float) newWidth) / width;
    float scaleHeight = ((float) newHeight) / height;
    // create a matrix for the manipulation
    Matrix matrix = new Matrix();
    // resize the bit map
    matrix.postScale(scaleWidth, scaleHeight);
    // recreate the new Bitmap
    Bitmap resizedBitmap = Bitmap.createBitmap(image, 0, 0, width, height,
            matrix, false);
    return resizedBitmap;
}
like image 81
Usman Kurd Avatar answered Nov 15 '22 01:11

Usman Kurd