开发者

Catching OutOfMemoryError in decoding Bitmap

Is it a good practice to catch OutOfMemoryError even you have tried some ways to reduce memory usage? Or should we just not catching the exception? Which one is better practice?

try {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = 4;
    bitmap = BitmapFactory.decodeFile(file, options);开发者_如何学JAVA
} catch (OutOfMemoryError e) {
    e.printStackTrace();
}

Thanks


It's good practice to catch it once and give decodeFile another chance. Catch it and call System.gc() and try decoding again. There is a high probability that it will work after calling System.gc().

try {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = 4;
    bitmap = BitmapFactory.decodeFile(file, options);
 } catch (OutOfMemoryError e) {
    e.printStackTrace();

    System.gc();

    try {
        bitmap = BitmapFactory.decodeFile(file);
    } catch (OutOfMemoryError e2) {
      e2.printStackTrace();
      // handle gracefully.
    }
}


I did something like this: I catch the error only for try to scale down the image until it works. Eventually it can not work at all; then returns null; otherwise, in success, returns the bitmap.

Outside I decide what to do with the bitmap whether it's null or not.

// Let w and h the width and height of the ImageView where we will place the Bitmap. Then:

// Get the dimensions of the original bitmap
BitmapFactory.Options bmOptions= new BitmapFactory.Options();
bmOptions.inJustDecodeBounds= true;
BitmapFactory.decodeFile(path, bmOptions);
int photoW= bmOptions.outWidth;
int photoH= bmOptions.outHeight;

// Determine how much to scale down the image. 
int scaleFactor= (int) Math.max(1.0, Math.min((double) photoW / (double)w, (double)photoH / (double)h));    //1, 2, 3, 4, 5, 6, ...
scaleFactor= (int) Math.pow(2.0, Math.floor(Math.log((double) scaleFactor) / Math.log(2.0)));               //1, 2, 4, 8, ...

// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds= false;
bmOptions.inSampleSize= scaleFactor;
bmOptions.inPurgeable= true;

do
{
    try
    {
        Log.d("tag", "scaleFactor: " + scaleFactor);
        scaleFactor*= 2;
        bitmap= BitmapFactory.decodeFile(path, bmOptions);
    }
    catch(OutOfMemoryError e)
    {
        bmOptions.inSampleSize= scaleFactor;
        Log.d("tag", "OutOfMemoryError: " + e.toString());
    }
}
while(bitmap == null && scaleFactor <= 256);

if(bitmap == null)
    return null;

For example, with an image of 3264x2448, the loop iterates 2 times on my phone, and then it works.


You'd want to catch it if you want to display either a smaller image / different image / show a custom error message to the user. Your image access wrapper can catch these errors and return some custom error codes defined within your code; your activity that uses this code can decide what to do with the error code - warn user, force him to exit with a better error message than the one the android system would provide, etc.

Btw, you are not using the options variable in your sample code.


Though it might not be a good idea to catch OutOfMemoryError using try-catch. But, sometimes you have no choice, because all of us hate app crashes. So, what you can do is

  1. Catch OutOfMemoryError using try-catch
  2. Since, after this error your activity may become unstable, restart it.
  3. You may disable animations so that user doesn't know that activity is restarted.
  4. You may put some extra data in intent to know that app was crashed during previous run.

How I did is:

    try {
        //code that causes OutOfMemoryError
    } catch (Exception e) {
        // in case of exception handle it
        e.printStackTrace();
    } catch (OutOfMemoryError oome)
    {
        //restart this activity
        Intent i=this.getIntent();
        i.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); //disable animation

        //EXTRA_ABNORMAL_SHUTDOWN is user defined       
        i.putExtra(this.EXTRA_ABNORMAL_SHUTDOWN, true);
        //put extra data into intent if you like

        finish(); //and finish the activity
        overridePendingTransition(0, 0);
        startActivity(i); //then start it(there is also restart method in newer API)
        return false;
    }

And then on onCreate of Activity you can resume(something like this):

boolean abnormalShutdown=getIntent().getBooleanExtra(this.EXTRA_ABNORMAL_SHUTDOWN, false);
if (abnormalShutdown)
{
    //Alert user for any error
    //get any extra data you put befor restarting.
}

This approach saved my app. Hope it helps you too!!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜