Catching OutOfMemoryError in decoding Bitmap

前端 未结 4 866
天命终不由人
天命终不由人 2020-12-13 14:30

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 pract

相关标签:
4条回答
  • 2020-12-13 14:53

    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.

    0 讨论(0)
  • 2020-12-13 14:57

    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.
        }
    }
    
    0 讨论(0)
  • 2020-12-13 15:05

    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 讨论(0)
  • 2020-12-13 15:09

    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.

    0 讨论(0)
提交回复
热议问题