Ошибка Android: java.lang.OutOfMemoryError: размер растрового изображения превышает бюджет виртуальной машины

Я столкнулся со многими вопросами в stackoverflow относительно этой ошибки, но ни один из них не нашел правильного решения для моего сценария.

В моем приложении для Android я должен разрешить пользователю нажимать кнопку, чтобы открыть галерею и выбрать изображение. А затем нужно загрузить это конкретное выбранное изображение в ImageView в моем макете (UI).

Делать это вполне нормально. Ниже приведен код, который я использую для достижения этой цели.

В кнопке Загрузить нажмите ->

        Intent intent = new Intent();
        intent.setType("image/*");
        intent.setAction(Intent.ACTION_GET_CONTENT);
        startActivityForResult(Intent.createChooser(intent,"Select Picture"), REQUEST_UPLOAD_IMG);

А затем onActivityResult ->

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) 
{       
    //super.onActivityResult(requestCode, resultCode, data);        
    if(resultCode == Activity.RESULT_OK)
    {
        if(requestCode==REQUEST_UPLOAD_IMG)
        {               
            Uri selectedImageURI = data.getData();
            uploadImgVW.setImageURI(selectedImageURI);              
        }
        else
        {
            Toast.makeText(MainActivity.this, "You can only select an Image.", Toast.LENGTH_LONG).show();
        }
    }
}

Но если пользователь выбирает изображение большего размера (например, 2 МБ), приложение завершает работу со следующей ошибкой. Но с нормальными (уровень КБ) изображениями все в порядке, и мне интересно, что я могу сделать для этой проблемы (чтобы справиться с этой ошибкой). Спасибо...

Ошибка ->

06-20 16:43:58.445: E/AndroidRuntime(2075): FATAL EXCEPTION: main
06-20 16:43:58.445: E/AndroidRuntime(2075): java.lang.OutOfMemoryError: bitmap size exceeds VM budget

person JibW    schedule 21.06.2013    source источник
comment
Возможно, вам следует перерабатывать его каждый раз при загрузке растрового изображения if(bitmap !=null){ bitmap.recycle();   -  person Mayank Saini    schedule 21.06.2013
comment
Приложение вылетает даже с первого раза. Это проблема. Я также добавил следующие коды, просто чтобы запустить сборщик мусора при нажатии кнопки. [Система.gc(); Время выполнения.getRuntime().gc(); ]   -  person JibW    schedule 21.06.2013
comment
Сбор мусора @JibW не проблема, вам нужно настроить размер изображения, как говорит Гуннар.   -  person tyczj    schedule 21.06.2013
comment
вы должны проверить этот вопрос, я опубликовал пример для эффективной загрузки больших растровых изображений: stackoverflow.com/questions/20692385/   -  person Zhar    schedule 16.04.2014


Ответы (3)


Существует ряд статей, описывающих эффективное управление растровыми изображениями. Глядя на свой код, вы загружаете изображение, не зная, насколько оно велико, и в конечном итоге вы столкнетесь с этими проблемами, особенно если вы загружаете и обрабатываете много изображений.

Идея, описанная в одной из этих статей, заключается в загрузке уже уменьшенного Bitmap ( сначала вы проверяете размер загружаемого изображения, затем вычисляете коэффициент масштабирования и только после этого загружаете уменьшенное изображение). Для этого вам нужно сначала узнать размеры ImageView, затем вам нужно будет использовать BitmapFactory.decode(...), так как у вас есть Uri целевого файла для отображения. Uri для файла должен быть тривиальным.

Кроме того, вам также нужно проверить потребление памяти в вашем приложении ... у вас могут быть другие ресурсы, которые висят в памяти, и вам нужно их очистить. Я использую очень полезный инструмент - MAT. Очень хорошую статью об этом можно найти здесь. Автор, Патрик Дубруа, провел очень интересную сессию на Google IO 2011 на эту тему. Посмотрите, для меня это было очень полезно...

person gunar    schedule 21.06.2013

измените размер изображения, а затем установите

public static Bitmap decodeUri(Context c, Uri uri, final int requiredSize) 
            throws FileNotFoundException {
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(c.getContentResolver().openInputStream(uri), null, o);

        int width_tmp = o.outWidth
                , height_tmp = o.outHeight;
        int scale = 1;

        while(true) {
            if(width_tmp / 2 < requiredSize || height_tmp / 2 < requiredSize)
                break;
            width_tmp /= 2;
            height_tmp /= 2;
            scale *= 2;
        }

        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        return BitmapFactory.decodeStream(c.getContentResolver().openInputStream(uri), null, o2);
    }   

или вы можете использовать так

if(resultCode == RESULT_OK){  
                Uri selectedImage = data.getData();
                InputStream imageStream = null;
                try {
                    imageStream = getContentResolver().openInputStream(selectedImage);
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                Bitmap yourSelectedImage = BitmapFactory.decodeStream(imageStream);
                profileImage.setImageBitmap(Bitmap.createScaledBitmap(yourSelectedImage , 120, 120, false));
                }
person Sunil Kumar    schedule 21.06.2013

Вы можете попробовать это...

BitmapFactory.Options options = new BitmapFactory.Options();
options.inTempStorage = new byte[16*1024];

Bitmap bitmapImage = BitmapFactory.decodeFile(path, options);
person Amresh    schedule 21.06.2013