Ловкость OutOfMemoryError в декодировании Растровое изображение
Является ли хорошей практикой поймать OutOfMemoryError, даже если вы попытались каким-то образом сократить использование памяти? Или мы просто не поймаем исключения? Какая из них лучше?
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
bitmap = BitmapFactory.decodeFile(file, options);
} catch (OutOfMemoryError e) {
e.printStackTrace();
}
Спасибо
Ответы
Ответ 1
Его хорошая практика - поймать его один раз и дать decodeFile
еще один шанс. Поймайте его и вызовите System.gc()
и повторите попытку декодирования. Существует высокая вероятность того, что он будет работать после вызова 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.
}
}
Ответ 2
Я сделал что-то вроде этого: я поймаю ошибку только для того, чтобы попытаться уменьшить изображение до тех пор, пока оно не сработает. В конце концов, он не может работать вообще; затем возвращает null; в противном случае, с успехом, возвращает растровое изображение.
Снаружи я решаю, что делать с растровым изображением, будь то null или нет.
// 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;
Например, с изображением 3264x2448 цикл повторяется 2 раза на моем телефоне, а затем он работает.
Ответ 3
Вы хотите поймать его, если хотите отобразить меньшее изображение/другое изображение/показать пользователю пользовательское сообщение об ошибке.
Ваша оболочка доступа к изображениям может уловить эти ошибки и вернуть некоторые пользовательские коды ошибок, определенные в вашем коде; ваша деятельность, использующая этот код, может решить, что делать с кодом ошибки - предупредить пользователя, заставить его выйти с лучшим сообщением об ошибке, чем тот, который предоставит система Android и т.д.
Btw, вы не используете переменную options в своем примере кода.
Ответ 4
Хотя может быть неплохо поймать OutOfMemoryError, используя try-catch. Но иногда у вас нет выбора, потому что все мы ненавидим атаки приложений.
Итак, что вы можете сделать, это
- Поймать OutOfMemoryError с помощью try-catch
- Так как после этой ошибки ваша активность может стать нестабильной, перезапустите ее.
- Вы можете отключить анимацию, чтобы пользователь не знал, что активность перезагружена.
- Вы можете поместить некоторые дополнительные данные в намерение знать, что приложение было разбито во время предыдущего запуска.
Как я это сделал:
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;
}
И затем в onCreate of Activity вы можете возобновить (что-то вроде этого):
boolean abnormalShutdown=getIntent().getBooleanExtra(this.EXTRA_ABNORMAL_SHUTDOWN, false);
if (abnormalShutdown)
{
//Alert user for any error
//get any extra data you put befor restarting.
}
Этот подход сохранил мое приложение.
Надеюсь, вам это тоже поможет.