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
- Catch OutOfMemoryError using try-catch
- Since, after this error your activity may become unstable, restart it.
- You may disable animations so that user doesn't know that activity is restarted.
- 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!!
精彩评论