Kevik
Kevik

Reputation: 9361

Out of memory error despite reducing image size android

This app was working with no problems but was too slow. It was running everything on the one UI thread, when a page loaded with many images in gridView it would take a long delay before the page would load. I fixed this problem using code from the android docs called "Processing Bitmaps Off the UI Thread"

Using the code it now loads the gridview page vary fast and after you can see it loading each small bitmap image one by one in a separate background thread.

In this code that uses AsyncTask for the multi-threading part it also uses a couple of methods to reduce the image size to eliminate out of memory errors. The same image reduction methods i used in the previous single threaded version of this program. No matter how big the image it should not be bigger than 200dp by 200dp set by the image reduction method. It works in the older version of the app without multi-threading, and it looks ok for the most part in the new version of the app. but there is a strange problem.

I am now getting out of memory errors connected to the BitmapFactory decode method.

How this happens is I started testing the app with many smaller images and put one huge one in there about 10,000 X 10,000 pixel bitmap. That should not be causing problems because the images are being reduced by using bitmap factory options.

The errors don't happen right away. The app works fine for a while but after moving around and going back an forth between activities. After 3 to 5 minutes it will crash.

Is anyone else having problems like this? And can you offer any ideas that might be the cause I could check out? Or help me to start looking in the right direction?

The image reducing code is shown here below and under that is the logcat output.

    class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
    private final WeakReference<ImageView> imageViewReference;
    private String data = "";

    public BitmapWorkerTask(ImageView imageView) {
        // Use a WeakReference to ensure the ImageView can be garbage collected
        imageViewReference = new WeakReference<ImageView>(imageView);
    }

    // Decode image in background.
    @Override
    protected Bitmap doInBackground(String... params) {
        data = params[0];
       // return imageProcessor(data);
        return decodeSampledBitmapFromResource(data, 100, 100);
    }

    // Once complete, see if ImageView is still around and set bitmap.
    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (imageViewReference != null && bitmap != null) {
            final ImageView imageView = imageViewReference.get();
            if (imageView != null) {
                imageView.setImageBitmap(bitmap);
            }
        }
    }
  }// end BitmapWorkerTask extends AsyncTask class

      public static Bitmap decodeSampledBitmapFromResource(String fileName, int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(fileName, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;

    return BitmapFactory.decodeFile(fileName, options);


}//end decodeSampledBitmapfromresource method


public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {
    if (width > height) {
        inSampleSize = Math.round((float)height / (float)reqHeight);
    } else {
        inSampleSize = Math.round((float)width / (float)reqWidth);
    }
}
return inSampleSize;
}// end calculateInSampleSize method


Logcat

17:52:28.340: E/AndroidRuntime(7812): FATAL EXCEPTION: main
17:52:28.340: E/AndroidRuntime(7812): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.v.splitter/com.v.splitter.AudioViewer}: android.view.InflateException: Binary XML file line #63: Error inflating class <unknown>
17:52:28.340: E/AndroidRuntime(7812): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1956)
17:52:28.340: E/AndroidRuntime(7812): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1981)
17:52:28.340: E/AndroidRuntime(7812): at android.app.ActivityThread.access$600(ActivityThread.java:123)
17:52:28.340: E/AndroidRuntime(7812): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1147)
17:52:28.340: E/AndroidRuntime(7812): at android.os.Handler.dispatchMessage(Handler.java:99)
17:52:28.340: E/AndroidRuntime(7812): at android.os.Looper.loop(Looper.java:137)
17:52:28.340: E/AndroidRuntime(7812): at android.app.ActivityThread.main(ActivityThread.java:4424)
17:52:28.340: E/AndroidRuntime(7812): at java.lang.reflect.Method.invokeNative(Native Method)
17:52:28.340: E/AndroidRuntime(7812): at java.lang.reflect.Method.invoke(Method.java:511)
17:52:28.340: E/AndroidRuntime(7812): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
17:52:28.340: E/AndroidRuntime(7812): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
17:52:28.340: E/AndroidRuntime(7812): at dalvik.system.NativeStart.main(Native Method)
17:52:28.340: E/AndroidRuntime(7812): Caused by: android.view.InflateException: Binary XML file line #63: Error inflating class <unknown>
17:52:28.340: E/AndroidRuntime(7812): at android.view.LayoutInflater.createView(LayoutInflater.java:606)
17:52:28.340: E/AndroidRuntime(7812): at com.android.internal.policy.impl.PhoneLayoutInflater.onCreateView(PhoneLayoutInflater.java:56)
17:52:28.340: E/AndroidRuntime(7812): at android.view.LayoutInflater.onCreateView(LayoutInflater.java:653)
17:52:28.340: E/AndroidRuntime(7812): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:678)
17:52:28.340: E/AndroidRuntime(7812): at android.view.LayoutInflater.rInflate(LayoutInflater.java:739)
17:52:28.340: E/AndroidRuntime(7812): at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
17:52:28.340: E/AndroidRuntime(7812): at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
17:52:28.340: E/AndroidRuntime(7812): at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
17:52:28.340: E/AndroidRuntime(7812): at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:255)
17:52:28.340: E/AndroidRuntime(7812): at android.app.Activity.setContentView(Activity.java:1835)
17:52:28.340: E/AndroidRuntime(7812): at com.v.splitter.AudioViewer.onCreate(AudioViewer.java:63)
17:52:28.340: E/AndroidRuntime(7812): at android.app.Activity.performCreate(Activity.java:4465)
17:52:28.340: E/AndroidRuntime(7812): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049)
17:52:28.340: E/AndroidRuntime(7812): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1920)
17:52:28.340: E/AndroidRuntime(7812): ... 11 more
17:52:28.340: E/AndroidRuntime(7812): Caused by: java.lang.reflect.InvocationTargetException
17:52:28.340: E/AndroidRuntime(7812): at java.lang.reflect.Constructor.constructNative(Native Method)
17:52:28.340: E/AndroidRuntime(7812): at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
17:52:28.340: E/AndroidRuntime(7812): at android.view.LayoutInflater.createView(LayoutInflater.java:586)
17:52:28.340: E/AndroidRuntime(7812): ... 24 more
12-17:52:28.340: E/AndroidRuntime(7812): Caused by: java.lang.OutOfMemoryError
17:52:28.340: E/AndroidRuntime(7812): at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
17:52:28.340: E/AndroidRuntime(7812): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:483)
17:52:28.340: E/AndroidRuntime(7812): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351)
17:52:28.340: E/AndroidRuntime(7812): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:773)
17:52:28.340: E/AndroidRuntime(7812): at android.content.res.Resources.loadDrawable(Resources.java:1937)
17:52:28.340: E/AndroidRuntime(7812): at android.content.res.TypedArray.getDrawable(TypedArray.java:601)
17:52:28.340: E/AndroidRuntime(7812): at android.view.View.<init>(View.java:2785)
17:52:28.340: E/AndroidRuntime(7812): at android.view.View.<init>(View.java:2722)
17:52:28.340: E/AndroidRuntime(7812): at android.view.ViewGroup.<init>(ViewGroup.java:379)
17:52:28.340: E/AndroidRuntime(7812): at android.widget.RelativeLayout.<init>(RelativeLayout.java:174)
17:52:28.340: E/AndroidRuntime(7812): ... 27 more

Upvotes: 0

Views: 849

Answers (1)

Andy McSherry
Andy McSherry

Reputation: 4725

A 10,000x10,000 image still needs to be loaded for BitmapFactory to resize. I'd suggest making that image smaller.

Anyway, if you're using a lot of Bitmaps, make sure to recycle them when you're finished with them. They consume native resources that garbage collection takes a while to get to.

bitmap.recycle();

Just make sure not to use the Bitmap after recycling because an exception will be thrown.

Upvotes: 2

Related Questions