Подтвердить что ты не робот

Ловкость OutOfMemoryError в декодировании Растровое изображение

Является ли хорошей практикой поймать OutOfMemoryError, даже если вы попытались каким-то образом сократить использование памяти? Или мы просто не поймаем исключения? Какая из них лучше?

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

Спасибо

4b9b3361

Ответ 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.
}

Этот подход сохранил мое приложение. Надеюсь, вам это тоже поможет.