Проблема с нехваткой памяти при загрузке изображения в растровый объект

1290

У меня есть представление списка с парой кнопок изображений в каждой строке. Когда вы щелкаете по строке списка, запускается новое действие. Мне пришлось создавать свои собственные вкладки из-за проблемы с макетом камеры. Действие, которое запускается для результата, является картой. Если я нажму на кнопку, чтобы запустить предварительный просмотр изображения (загрузить изображение с SD-карты), приложение вернется из действия обратно в listviewдействие в обработчик результатов, чтобы повторно запустить мое новое действие, которое является не более чем виджетом изображения.

Предварительный просмотр изображения в виде списка выполняется с помощью курсора и ListAdapter. Это делает это довольно просто, но я не уверен, как я могу поместить изображение с измененным размером (т.е. меньший размер бит, а не пиксель, как srcдля кнопки изображения на лету. Поэтому я просто изменил размер изображения, полученного с камеры телефона.

Проблема в том, что я получаю ошибку «Недостаточно памяти» при попытке вернуться и перезапустить 2-е действие.

  • Есть ли способ, которым я могу легко построить список адаптер за строкой, где я могу изменить размер на лету ( немного мудрее )?

Это было бы предпочтительным, поскольку мне также необходимо внести некоторые изменения в свойства виджетов / элементов в каждой строке, поскольку я не могу выбрать строку с сенсорным экраном из-за проблемы с фокусировкой. ( Я могу использовать роллер мяч. )

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

Как только я отключил изображение в списке, оно снова заработало.

К вашему сведению: вот как я это делал:

String[] from = new String[] { DBHelper.KEY_BUSINESSNAME,DBHelper.KEY_ADDRESS,DBHelper.KEY_CITY,DBHelper.KEY_GPSLONG,DBHelper.KEY_GPSLAT,DBHelper.KEY_IMAGEFILENAME  + ""};
int[] to = new int[] {R.id.businessname,R.id.address,R.id.city,R.id.gpslong,R.id.gpslat,R.id.imagefilename };
notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);

Где R.id.imagefilenameнаходитсяButtonImage .

Вот мой LogCat:

01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process.
01-25 05:05:49.877: ERROR/(3896): VM wont let us allocate 6291456 bytes
01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.resolveUri(ImageView.java:484)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.setImageURI(ImageView.java:281)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.CursorAdapter.getView(CursorAdapter.java:150)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.obtainView(AbsListView.java:1057)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.makeAndAddView(ListView.java:1616)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.fillSpecific(ListView.java:1177)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.layoutChildren(ListView.java:1454)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.onLayout(AbsListView.java:937)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:922)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:920)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.performTraversals(ViewRoot.java:771)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1103)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Handler.dispatchMessage(Handler.java:88)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Looper.loop(Looper.java:123)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.app.ActivityThread.main(ActivityThread.java:3742)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invokeNative(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invoke(Method.java:515)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at dalvik.system.NativeStart.main(Native Method)
01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed 

У меня также есть новая ошибка при отображении изображения:

01-25 22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d
01-25 22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri: 
01-25 22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process.
01-25 22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes
01-25 22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed
Chrispix
источник
8
Я решил это, избегая Bitmap.decodeStream или decodeFile и используя метод BitmapFactory.decodeFileDescriptor.
Fraggle
1
Я также столкнулся с подобной проблемой пару недель назад, и я решил ее, уменьшив изображения до оптимальной точки. Я написал полный подход в своем блоге codingjunkiesforum.wordpress.com/2014/06/12/… и загрузил полный пример проекта с кодом OOM против кода подтверждения OOM athttps: //github.com/shailendra123/BitmapHandlingDemo
Shailendra Singh Rajawat
5
Принятая ответ на этот вопрос в настоящее время обсуждается на мета
Rene
4
Это происходит, когда вы не читаете руководства для разработчиков Android
Педро Варела
2
Это происходит из-за плохой архитектуры Android. Он должен сам изменять размеры изображений, как это делает ios, и UWP делает это. Я не должен делать это сам. Разработчики Android привыкли к этому черту и думают, что он работает так, как должен.
Доступ запрещен

Ответы:

651

Android обучение класс, « Отображение растровых изображений эффективны », предлагает некоторую большую информацию для понимания и решения , за исключением java.lang.OutOfMemoryError: bitmap size exceeds VM budgetпри загрузке Bitmaps.


Читать растровые размеры и тип

BitmapFactoryКласс предоставляет несколько методов декодирования ( decodeByteArray(), decodeFile(), decodeResource()и т.д.) для создания Bitmapиз различных источников. Выберите наиболее подходящий метод декодирования в зависимости от источника данных вашего изображения. Эти методы пытаются выделить память для созданного растрового изображения и поэтому могут легко привести к OutOfMemoryисключению. Каждый тип метода декодирования имеет дополнительные подписи, которые позволяют вам указать опции декодирования через BitmapFactory.Optionsкласс. Установка inJustDecodeBoundsсвойства во trueвремя декодирования позволяет избежать выделения памяти, возвращая nullобъект растрового изображения, кроме установки outWidth, outHeightи outMimeType. Этот метод позволяет считывать размеры и тип данных изображения до создания (и выделения памяти) растрового изображения.

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

Чтобы избежать java.lang.OutOfMemoryисключений, проверьте размеры растрового изображения перед его декодированием, если только вы не доверяете источнику, предоставляя вам данные изображения предсказуемого размера, которые удобно помещаются в доступную память.


Загрузить уменьшенную версию в память

Теперь, когда размеры изображения известны, их можно использовать, чтобы решить, следует ли загружать полное изображение в память или вместо этого следует загружать версию с субдискретизацией. Вот некоторые факторы, которые следует учитывать:

  • Предполагаемое использование памяти при загрузке полного изображения в память.
  • Объем памяти, который вы готовы выделить для загрузки этого изображения, учитывая любые другие требования к памяти вашего приложения.
  • Размеры целевого компонента ImageView или пользовательского интерфейса, в который должно быть загружено изображение.
  • Размер экрана и плотность текущего устройства.

Например, не стоит загружать изображение 1024x768 пикселей в память, если оно в конечном итоге будет отображаться в виде миниатюры 128x96 пикселей в ImageView .

Для того, чтобы сказать , декодер субсемплировать изображение, загружая уменьшенную версию в память, набор inSampleSizeдля trueвашего BitmapFactory.Optionsобъекта. Например, изображение с разрешением 2048x1536, которое декодируется с inSampleSize4, создает растровое изображение приблизительно 512x384. Загрузка этого в память использует 0,75 МБ, а не 12 МБ для полного изображения (при условии конфигурации растрового изображения ARGB_8888). Вот метод для вычисления значения размера выборки, которое является степенью двойки, на основе целевой ширины и высоты:

public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

Примечание . Значение степени два вычисляется, поскольку декодер использует конечное значение путем округления до ближайшего значения степени два, как указано в inSampleSizeдокументации.

Чтобы использовать этот метод, сначала inJustDecodeBoundsвыполните декодирование с установленным значением true, пропустите параметры, а затем снова выполните декодирование с использованием нового inSampleSizeзначения и inJustDecodeBoundsустановите на false:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
    int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

Этот метод позволяет легко загрузить растровое изображение произвольно большого размера в файл, ImageViewкоторый отображает миниатюру 100x100 пикселей, как показано в следующем примере кода:

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

Вы можете выполнить аналогичный процесс для декодирования растровых изображений из других источников, подставив соответствующий BitmapFactory.decode*метод по мере необходимости.

Sazid
источник
21
Ответ на этот вопрос в настоящее время обсуждается на мета
Rene
9
Этот ответ (кроме информации, полученной по ссылке) не предлагает решения, как для ответа. Важные части ссылки должны быть объединены в вопросе.
FallenAngel
7
Этот ответ, как и вопрос, и другие ответы - это Wiki сообщества, так что это то, что сообщество может исправить путем редактирования, что не требует вмешательства модератора.
Мартин Питерс
текущую ссылку на контент и поддержку Kotlin можно найти по адресу: developer.android.com/topic/performance/graphics/load-bitmap
Panos Gr
891

Чтобы исправить ошибку OutOfMemory, вы должны сделать что-то вроде этого:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap = BitmapFactory.decodeStream(is, null, options);

Эта inSampleSizeопция уменьшает потребление памяти.

Вот полный метод. Сначала он читает размер изображения без декодирования самого содержимого. Затем он находит лучшее inSampleSizeзначение, оно должно быть степенью 2, и, наконец, изображение декодируется.

// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f), null, o);

        // The new size we want to scale to
        final int REQUIRED_SIZE=70;

        // Find the correct scale value. It should be the power of 2.
        int scale = 1;
        while(o.outWidth / scale / 2 >= REQUIRED_SIZE && 
              o.outHeight / scale / 2 >= REQUIRED_SIZE) {
            scale *= 2;
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {}
    return null;
}
Федор
источник
31
Обратите внимание, что 10 может быть не лучшим значением для inSampleSize, хотя в документации предлагается использовать степени 2.
Мирко Н.
70
Я сталкиваюсь с той же проблемой, что и Chrispix, но я не думаю, что решение здесь действительно решает проблему, а скорее обходит ее. Изменение размера выборки уменьшает объем используемой памяти (за счет качества изображения, которое, вероятно, подходит для предварительного просмотра изображения), но это не предотвратит исключение, если декодируется достаточно большой поток изображений, или если несколько потоков изображений декодируется. Если я найду лучшее решение (а может и не найти), я выложу ответ здесь.
Flynn81
4
Вам нужен только соответствующий размер, чтобы соответствовать экрану с плотностью пикселей, для увеличения, и вы можете взять образец изображения с более высокой плотностью.
стелскоптер
4
REQUIRED_SIZE - это новый размер, который вы хотите масштабировать.
Федор
8
Это решение помогло мне, но качество изображения ужасное. Я использую видоискатель для отображения изображений какие-либо предложения?
user1106888
373

Я сделал небольшое улучшение в коде Федора. Он в основном делает то же самое, но без (на мой взгляд) безобразного цикла while, и это всегда приводит к степени двойки. Слава Федору за принятие оригинального решения, я застрял, пока не нашел его, а потом смог сделать это :)

 private Bitmap decodeFile(File f){
    Bitmap b = null;

        //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;

    FileInputStream fis = new FileInputStream(f);
    BitmapFactory.decodeStream(fis, null, o);
    fis.close();

    int scale = 1;
    if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
        scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / 
           (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
    }

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize = scale;
    fis = new FileInputStream(f);
    b = BitmapFactory.decodeStream(fis, null, o2);
    fis.close();

    return b;
}
Thomas Vervest
источник
40
Да ты прав пока не так красиво. Я просто пытался объяснить это всем. Спасибо за ваш код.
Федор
10
@Tommas Vervest - есть большая проблема с этим кодом. ^ не возводит 2 в степень, это xors 2 с результатом. Вы хотите Math.pow (2.0, ...). В противном случае это выглядит хорошо.
Дугв
6
О, это очень хорошо! Плохо, исправлю сразу, спасибо за ответ!
Томас Вервест
8
Вы создаете два новых FileInputStreams, по одному для каждого вызова BitmapFactory.decodeStream(). Не нужно ли сохранять ссылку на каждый из них, чтобы они могли быть закрыты в finallyблоке?
Мацев
1
@Babibu В документации не говорится, что поток закрыт для вас, поэтому я предполагаю, что он все еще должен быть закрыт. Интересное и связанное обсуждение можно найти здесь . Обратите внимание на комментарий Адриана Смита, который имеет непосредственное отношение к нашей дискуссии.
Томас Вервест
233

Я пришел из опыта iOS и был разочарован, обнаружив проблему с чем-то таким простым, как загрузка и показ изображения. В конце концов, каждый, кто имеет эту проблему, пытается отобразить изображения разумного размера. В любом случае, вот два изменения, которые исправили мою проблему (и сделали мое приложение очень отзывчивым).

1) Каждый раз, когда вы делаете BitmapFactory.decodeXYZ(), убедитесь, что передаете BitmapFactory.Optionsс inPurgeableустановленным на true(и предпочтительно с inInputShareableтакже установленным наtrue ).

2) НИКОГДА не пользуйтесь Bitmap.createBitmap(width, height, Config.ARGB_8888). Я имею в виду НИКОГДА! У меня никогда не было, чтобы это не вызывало ошибку памяти после нескольких проходов. Никакое количество recycle(), System.gc()независимо не помогло. Это всегда вызывало исключение. Еще один способ, который на самом деле работает, заключается в том, чтобы иметь фиктивное изображение в ваших чертежах (или другое растровое изображение, которое вы декодировали с помощью шага 1, описанного выше), измените его масштаб на любое значение, а затем манипулируйте полученным растровым изображением (например, передав его на холст). для большего удовольствия). Итак, что вы должны использовать вместо этого: Bitmap.createScaledBitmap(srcBitmap, width, height, false). Если по какой-либо причине вы ДОЛЖНЫ использовать метод создания грубой силы, то хотя бы проходите Config.ARGB_4444.

Это почти гарантированно сэкономит вам часы, если не дни. Все, что говорит о масштабировании изображения и т. Д., На самом деле не работает (если только вы не решите получить неправильный размер или ухудшенное изображение).

Ephraim
источник
22
BitmapFactory.Options options = new BitmapFactory.Options(); options.inPurgeable = true;и Bitmap.createScaledBitmap(srcBitmap, width, height, false);решил мою проблему, которую я имел с исключением нехватки памяти на Android 4.0.0. Спасибо друг!
Ян-Терье Соренсен
5
В вызове Bitmap.createScaledBitmap () вы, вероятно, должны использовать true в качестве параметра flag. В противном случае качество изображения не будет плавным при увеличении. Проверьте эту тему stackoverflow.com/questions/2895065/...
rOrlig
11
Это действительно потрясающий совет. Жаль, что я не мог бы дать вам +1 за то, что вы взяли Google на работу за эту удивительно странную ошибку. Я имею в виду ... если это не ошибка, то в документации действительно должны быть какие-то серьёзно мигающие неоновые вывески с надписью "ЭТО КАК ВЫ РАБОТАЕТЕ С ФОТОГРАФИЯМИ", потому что я боролся с этим 2 года и только сейчас нашел этот пост. Отличная находка.
Евгений Симкин
Определенно помогает уменьшение ваших изображений, но это важный шаг, и в конечном итоге это решило эту проблему для меня. Проблема с простым масштабированием ваших изображений заключается в том, что если у вас их много или исходные изображения очень большие, вы все равно можете столкнуться с той же проблемой. +1 тебе, Ефрем.
Дейв
10
По состоянию на Lollipop BitmapFactory.Options.inPurgeableи BitmapFactory.Options.inInputShareableустарели developer.android.com/reference/android/graphics/…
Денис Княжев
93

Это известная ошибка , это не из-за больших файлов. Поскольку Android кэширует Drawables, он исчерпывает память после использования нескольких изображений. Но я нашел для этого альтернативный способ, пропустив систему кеширования Android по умолчанию.

Решение . Переместите изображения в папку «assets» и используйте следующую функцию для получения BitmapDrawable:

public static Drawable getAssetImage(Context context, String filename) throws IOException {
    AssetManager assets = context.getResources().getAssets();
    InputStream buffer = new BufferedInputStream((assets.open("drawable/" + filename + ".png")));
    Bitmap bitmap = BitmapFactory.decodeStream(buffer);
    return new BitmapDrawable(context.getResources(), bitmap);
}
Anto Binish Kaspar
источник
79

У меня была та же проблема, и я решил ее, избегая функций BitmapFactory.decodeStream или decodeFile, и вместо этого использовал BitmapFactory.decodeFileDescriptor

decodeFileDescriptor похоже, что он вызывает другие нативные методы, чем decodeStream / decodeFile.

В любом случае, что сработало, так это (обратите внимание, что я добавил некоторые параметры, как некоторые имели выше, но это не то, что имело значение. Что важно, так это вызов BitmapFactory.decodeFileDescriptor вместо decodeStream или decodeFile ):

private void showImage(String path)   {

    Log.i("showImage","loading:"+path);
    BitmapFactory.Options bfOptions=new BitmapFactory.Options();
    bfOptions.inDither=false;                     //Disable Dithering mode
    bfOptions.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
    bfOptions.inInputShareable=true;              //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
    bfOptions.inTempStorage=new byte[32 * 1024]; 

    File file=new File(path);
    FileInputStream fs=null;
    try {
        fs = new FileInputStream(file);
    } catch (FileNotFoundException e) {
        //TODO do something intelligent
        e.printStackTrace();
    }

    try {
        if(fs!=null) bm=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
    } catch (IOException e) {
        //TODO do something intelligent
        e.printStackTrace();
    } finally{ 
        if(fs!=null) {
            try {
                fs.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    //bm=BitmapFactory.decodeFile(path, bfOptions); This one causes error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget

    im.setImageBitmap(bm);
    //bm.recycle();
    bm=null;                        
}

Я думаю, что есть проблема с нативной функцией, используемой в decodeStream / decodeFile. Я подтвердил, что при использовании decodeFileDescriptor вызывается другой нативный метод. Кроме того, я прочитал, что «изображения (битовые карты) распределяются не стандартным способом Java, а с помощью собственных вызовов; выделения выполняются вне виртуальной кучи, но учитываются! »

Fraggle
источник
1
тот же результат из памяти, фактически не имеет значения, какой метод вы используете, зависит от количества байтов, которые вы держите для чтения данных, которые выдают из памяти.
ПиюшМишра
72

Я думаю, что лучший способ избежать этого OutOfMemoryError- это смириться с этим и понять.

Я сделал приложение, чтобы преднамеренно вызватьOutOfMemoryError и контролировать использование памяти.

После того, как я провел много экспериментов с этим приложением, я сделал следующие выводы:

Сначала я расскажу о версиях SDK перед Honey Comb.

  1. Растровое изображение хранится в собственной куче, но оно будет собирать мусор автоматически, вызывая recycle () не нужно.

  2. Если {Размер кучи виртуальной машины} + {выделенная собственная память кучи}> = {Ограничение размера кучи виртуальной машины для устройства}, и вы пытаетесь создать растровое изображение, OOM будет выброшено.

    УВЕДОМЛЕНИЕ: размер виртуальной машины VM считается, а не выделенная памятью.

  3. Размер кучи виртуальной машины никогда не будет уменьшаться после увеличения, даже если выделенная память виртуальной машины уменьшается.

  4. Таким образом, вы должны поддерживать максимальный объем памяти виртуальной машины как можно ниже, чтобы размер кучи виртуальных машин не становился слишком большим для сохранения доступной памяти для растровых изображений.

  5. Вручную вызывать System.gc () бессмысленно, система сначала вызовет его, прежде чем пытаться увеличить размер кучи.

  6. Собственный размер кучи тоже никогда не сократится, но это не считается OOM, так что об этом не нужно беспокоиться.

Тогда давайте поговорим о SDK Starts от Honey Comb.

  1. Растровое изображение хранится в куче виртуальных машин, собственная память не учитывается для OOM.

  2. Условие для OOM намного проще: {размер кучи виртуальной машины}> = {ограничение размера кучи виртуальной машины для устройства}.

  3. Таким образом, у вас есть больше доступной памяти для создания растрового изображения с тем же пределом размера кучи, OOM с меньшей вероятностью будет выброшен.

Вот некоторые из моих наблюдений о сборке мусора и утечке памяти.

Вы можете увидеть это сами в приложении. Если Activity выполнил AsyncTask, которая все еще работала после того, как Activity была уничтожена, Activity не будет собирать мусор до завершения AsyncTask.

Это потому, что AsyncTask является экземпляром анонимного внутреннего класса, он содержит ссылку на Activity.

Вызов AsyncTask.cancel (true) не остановит выполнение, если задача заблокирована в операции ввода-вывода в фоновом потоке.

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

Если вы запланировали повторяющуюся или отложенную задачу, например, Timer, и не вызывали cancel () и purge () в onPause (), память просочилась.

coocood
источник
AsyncTask не обязательно должен быть «экземпляром анонимного внутреннего класса», и то же самое касается Callbackks. Вы можете создать новый открытый класс в своем собственном файле, который расширяет AsyncTask, или даже private static classв том же классе. Они не будут иметь никаких упоминаний об Деятельности (если вы не дадите им одно из них, конечно)
Саймон Форсберг,
65

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

Из-за этого я написал пример приложения, которое демонстрирует кеширование в среде Android. Эта реализация еще не получила OOM.

Посмотрите на конец этого ответа для ссылки на исходный код.

Требования:

  • Android API 2.1 или выше (мне просто не удалось получить доступную память для приложения в API 1.6 - это единственный фрагмент кода, который не работает в API 1.6)
  • Пакет поддержки Android

Скриншот

Особенности:

  • Сохраняет кеш при изменении ориентации , используя синглтон
  • Используйте одну восьмую назначенной памяти приложения для кэша (измените, если хотите)
  • Большие растровые изображения масштабируются (вы можете определить максимальное количество пикселей, которое вы хотите разрешить)
  • Контролирует наличие доступного интернет-соединения перед загрузкой растровых изображений
  • Уверен, что вы создаете только одну задачу на строку
  • Если вы отбросив в ListViewсторону, он просто не будет загружать растровые изображения между

Это не включает в себя:

  • Кэширование диска. В любом случае это должно быть легко реализовать - просто укажите на другую задачу, которая получает растровые изображения с диска

Образец кода:

Загружаемые изображения - это изображения (75x75) с Flickr. Однако поместите любые URL-адреса изображений, которые вы хотите обработать, и приложение уменьшит их, если они превысят максимум. В этом приложении URL-адреса просто в Stringмассиве.

LruCacheИмеет хороший способ иметь дело с растровыми изображениями. Тем не менее, в этом приложении я поместил экземпляр LruCacheвнутри другого кеш-класса, который я создал, чтобы сделать приложение более выполнимым.

Критические вещи Cache.java ( loadBitmap()метод является наиболее важным):

public Cache(int size, int maxWidth, int maxHeight) {
    // Into the constructor you add the maximum pixels
    // that you want to allow in order to not scale images.
    mMaxWidth = maxWidth;
    mMaxHeight = maxHeight;

    mBitmapCache = new LruCache<String, Bitmap>(size) {
        protected int sizeOf(String key, Bitmap b) {
            // Assuming that one pixel contains four bytes.
            return b.getHeight() * b.getWidth() * 4;
        }
    };

    mCurrentTasks = new ArrayList<String>();    
}

/**
 * Gets a bitmap from cache. 
 * If it is not in cache, this method will:
 * 
 * 1: check if the bitmap url is currently being processed in the
 * BitmapLoaderTask and cancel if it is already in a task (a control to see
 * if it's inside the currentTasks list).
 * 
 * 2: check if an internet connection is available and continue if so.
 * 
 * 3: download the bitmap, scale the bitmap if necessary and put it into
 * the memory cache.
 * 
 * 4: Remove the bitmap url from the currentTasks list.
 * 
 * 5: Notify the ListAdapter.
 * 
 * @param mainActivity - Reference to activity object, in order to
 * call notifyDataSetChanged() on the ListAdapter.
 * @param imageKey - The bitmap url (will be the key).
 * @param imageView - The ImageView that should get an
 * available bitmap or a placeholder image.
 * @param isScrolling - If set to true, we skip executing more tasks since
 * the user probably has flinged away the view.
 */
public void loadBitmap(MainActivity mainActivity, 
        String imageKey, ImageView imageView,
        boolean isScrolling) {
    final Bitmap bitmap = getBitmapFromCache(imageKey); 

    if (bitmap != null) {
        imageView.setImageBitmap(bitmap);
    } else {
        imageView.setImageResource(R.drawable.ic_launcher);
        if (!isScrolling && !mCurrentTasks.contains(imageKey) && 
                mainActivity.internetIsAvailable()) {
            BitmapLoaderTask task = new BitmapLoaderTask(imageKey,
                    mainActivity.getAdapter());
            task.execute();
        }
    } 
}

Вам не нужно ничего редактировать в файле Cache.java, если вы не хотите реализовать кэширование на диске.

Критический материал MainActivity.java:

public void onScrollStateChanged(AbsListView view, int scrollState) {
    if (view.getId() == android.R.id.list) {
        // Set scrolling to true only if the user has flinged the       
        // ListView away, hence we skip downloading a series
        // of unnecessary bitmaps that the user probably
        // just want to skip anyways. If we scroll slowly it
        // will still download bitmaps - that means
        // that the application won't wait for the user
        // to lift its finger off the screen in order to
        // download.
        if (scrollState == SCROLL_STATE_FLING) {
            mIsScrolling = true;
        } else {
            mIsScrolling = false;
            mListAdapter.notifyDataSetChanged();
        }
    } 
}

// Inside ListAdapter...
@Override
public View getView(final int position, View convertView, ViewGroup parent) {           
    View row = convertView;
    final ViewHolder holder;

    if (row == null) {
        LayoutInflater inflater = getLayoutInflater();
        row = inflater.inflate(R.layout.main_listview_row, parent, false);  
        holder = new ViewHolder(row);
        row.setTag(holder);
    } else {
        holder = (ViewHolder) row.getTag();
    }   

    final Row rowObject = getItem(position);

    // Look at the loadBitmap() method description...
    holder.mTextView.setText(rowObject.mText);      
    mCache.loadBitmap(MainActivity.this,
            rowObject.mBitmapUrl, holder.mImageView,
            mIsScrolling);  

    return row;
}

getView()очень часто звонят. Обычно не стоит загружать изображения туда, если мы не реализовали проверку, которая гарантирует, что мы не будем запускать бесконечное количество потоков в строке. Cache.java проверяет, есть ли rowObject.mBitmapUrlуже в задаче, и если это так, он не запускает другую. Поэтому мы, скорее всего, не превышаем ограничение рабочей очереди из AsyncTaskпула.

Скачать:

Вы можете скачать исходный код с https://www.dropbox.com/s/pvr9zyl811tfeem/ListViewImageCache.zip .


Последние слова:

Я проверил это в течение нескольких недель, я еще не получил ни одного исключения OOM. Я проверил это на эмуляторе, на своем Nexus One и на Nexus S. Я протестировал URL-адреса изображений, которые содержат изображения в качестве HD. Единственным узким местом является то, что загрузка занимает больше времени.

Есть только один возможный сценарий, когда я могу представить себе, что OOM появится, и это если мы загрузим много действительно больших изображений и, прежде чем они будут масштабированы и помещены в кэш, будут одновременно занимать больше памяти и вызывать OOM. Но в любом случае это даже не идеальная ситуация, и, скорее всего, ее невозможно будет решить более приемлемым способом.

Сообщить об ошибках в комментариях! :-)

вроцлай
источник
43

Я сделал следующее, чтобы взять изображение и изменить его размер на лету. Надеюсь это поможет

Bitmap bm;
bm = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(filepath), 100, 100, true);
mPicture = new ImageView(context);
mPicture.setImageBitmap(bm);    
Chrispix
источник
26
Этот подход масштабирует растровое изображение. Но это не решает проблему OutOfMemory, потому что в любом случае декодируется полное растровое изображение.
Федор
5
Я посмотрю, смогу ли я посмотреть на свой старый код, но думаю, что он решил мои проблемы с нехваткой памяти. Буду перепроверять мой старый код.
Chrispix
2
В этом примере, по крайней мере, похоже, что вы не сохраняете ссылку на полное растровое изображение, следовательно, экономия памяти.
NoBugs
Для меня это решило проблему с памятью, но снизило качество изображений.
Памела Силла,
35

Кажется, что это очень длительная проблема, с множеством разных объяснений. Я воспользовался советом двух наиболее распространенных представленных здесь ответов, но ни один из них не решил мои проблемы с виртуальной машиной, утверждая, что она не может позволить байтам выполнить часть процесса декодирования . После некоторого копания я узнал, что настоящая проблема здесь - процесс декодирования, убирающий из кучи NATIVE .

Смотрите здесь: BitmapFactory OOM сводит меня с ума

Это привело меня к другой ветке обсуждения, где я нашел еще пару решений этой проблемы. Один из них - звонить System.gc();вручную после отображения вашего изображения. Но это на самом деле заставляет ваше приложение использовать БОЛЬШЕ памяти, чтобы уменьшить собственную кучу. Наилучшим решением с версии 2.0 (Donut) является использование опции BitmapFactory «inPurgeable». Так что я просто добавил o2.inPurgeable=true;сразу после o2.inSampleSize=scale;.

Подробнее об этом здесь: лимит кучи памяти только 6M?

Теперь, сказав все это, я тоже полный дурак с Java и Android. Так что, если вы думаете, что это ужасный способ решить эту проблему, вы, вероятно, правы. ;-) Но это творило чудеса для меня, и я нашел невозможным запустить виртуальную машину из кеш-памяти. Единственный недостаток, который я могу найти, это то, что вы уничтожаете свое кэшированное нарисованное изображение. Это означает, что если вы вернетесь назад к этому изображению, вы будете перерисовывать его каждый раз. В случае, как работает мое приложение, это не проблема. Ваш пробег может варьироваться.

оборота RayHaque
источник
inPurgeable исправил OOM для меня.
Артем Руссаковский
35

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

 <application
         android:largeHeap="true"
Himanshu Mori
источник
1
Можете ли вы объяснить, что это на самом деле делает? Просто сказать людям добавить это не помогает.
Раввин Стелс
1
Это очень плохое решение. По сути, вы не пытаетесь решить проблему. Вместо этого попросите систему Android выделить больше места для вашего приложения. Это будет иметь очень плохие последствия для вашего приложения, например, для вашего приложения, потребляющего много энергии батареи, поскольку GC должен пробежать через большое пространство кучи для очистки памяти, а также производительность вашего приложения будет ниже.
Пракаш
2
тогда почему андроид позволяет нам добавить этот андроид: largeHeap = "true" в нашем манифесте? Теперь вы бросаете вызов Android.
Химаншу Мори
32

Используйте это. bitmap.recycle();Это помогает без проблем с качеством изображения.

Arsalan
источник
9
Согласно API, вызов recycle () не нужен.
Артем Руссаковский
28

У меня есть гораздо более эффективное решение, которое не требует масштабирования. Просто расшифруйте ваш растровый файл только один раз, а затем кешируйте его на карте по его имени. Затем просто получите растровое изображение по имени и установите его в ImageView. Больше ничего не нужно делать.

Это будет работать, потому что фактические двоичные данные декодированного растрового изображения не сохраняются в куче виртуальной машины dalvik. Хранится снаружи. Поэтому каждый раз, когда вы декодируете растровое изображение, он выделяет память вне кучи виртуальной машины, которая никогда не возвращается GC

Чтобы лучше это оценить, представьте, что вы сохранили свое изображение в папке для рисования. Вы просто получаете изображение, выполняя getResources (). GetDrwable (R.drawable.). Это НЕ будет декодировать ваше изображение каждый раз, но будет повторно использовать уже декодированный экземпляр каждый раз, когда вы его вызываете. Так что по сути это кешируется.

Теперь, поскольку ваше изображение находится в файле где-то (или, возможно, даже поступает с внешнего сервера), вы несете ответственность за кэширование декодированного экземпляра растрового изображения для повторного использования в любом месте, где это необходимо.

Надеюсь это поможет.

Парт Мехта
источник
4
"и затем кешировать его на карте против его имени." Как именно вы кэшируете свои изображения?
Винсент
3
Вы действительно пробовали это? Несмотря на то, что данные о пикселях фактически не хранятся в куче Dalvik, их размер в собственной памяти передается на виртуальную машину и учитывается в ее доступной памяти.
ErikR
3
@ Vincent Я думаю, это не сложно хранить их на карте. Я бы предложил что-то вроде карты HashMap <KEY, Bitmap>, где ключ может быть строкой источника или чем-то, что имеет смысл для вас. Предположим, вы берете путь как KEY, сохраняете его как map.put (Path, Bitmap) и получаете его через map.get (Path)
Рафаэль Т
3
вы, вероятно, захотите использовать HashMap <String, SoftReference <Bitmap >>, если вы реализуете кэш изображений, в противном случае вы можете все равно исчерпать память - также я не думаю, что «он выделяет память вне кучи VM, которая никогда не восстанавливается GC «Это правда, память восстанавливается, как я понимаю, может быть задержка, для чего и используется bitmap.recycle (), как подсказка для восстановления памяти раньше ...
Дори
28

Я решил ту же проблему следующим образом.

Bitmap b = null;
Drawable d;
ImageView i = new ImageView(mContext);
try {
    b = Bitmap.createBitmap(320,424,Bitmap.Config.RGB_565);
    b.eraseColor(0xFFFFFFFF);
    Rect r = new Rect(0, 0,320 , 424);
    Canvas c = new Canvas(b);
    Paint p = new Paint();
    p.setColor(0xFFC0C0C0);
    c.drawRect(r, p);
    d = mContext.getResources().getDrawable(mImageIds[position]);
    d.setBounds(r);
    d.draw(c);

    /*   
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inTempStorage = new byte[128*1024];
        b = BitmapFactory.decodeStream(mContext.getResources().openRawResource(mImageIds[position]), null, o2);
        o2.inSampleSize=16;
        o2.inPurgeable = true;
    */
} catch (Exception e) {

}
i.setImageBitmap(b);
Prerna
источник
все в порядке, но я использую несколько растровых изображений для рисования круга в OnCreate и вызове действия 4-5 раз, так как очистить растровое изображение и как удалить растровое изображение и создать его снова во второй раз, когда задание 0nCreate ..
ckpatel
27

Здесь есть два вопроса ....

  • Память растрового изображения находится не в куче виртуальной машины, а в собственной куче - см. BitmapFactory OOM сводит меня с ума
  • Сборка мусора для собственной кучи ленивее, чем кучи виртуальных машин, поэтому вам нужно быть очень агрессивным в отношении выполнения bitmap.recycle и bitmap = null каждый раз, когда вы проходите через Activity onPause или onDestroy
Торид
источник
Он находится в куче виртуальных
машин
27

Это сработало для меня!

public Bitmap readAssetsBitmap(String filename) throws IOException {
    try {
        BitmapFactory.Options options = new BitmapFactory.Options(); 
        options.inPurgeable = true;
        Bitmap bitmap = BitmapFactory.decodeStream(assets.open(filename), null, options);
        if(bitmap == null) {
            throw new IOException("File cannot be opened: It's value is null");
        } else {
            return bitmap;
        }
    } catch (IOException e) {
        throw new IOException("File cannot be opened: " + e.getMessage());
    }
}
Люк Тейлор
источник
20

Ни один из приведенных выше ответов не помог мне, но я нашел ужасно уродливый обходной путь, который решил проблему. Я добавил очень маленькое изображение размером 1x1 пиксель в свой проект в качестве ресурса и загрузил его в свой ImageView перед вызовом в сборщик мусора. Я думаю, что, возможно, ImageView не выпускал растровое изображение, поэтому GC никогда не брал его. Это некрасиво, но пока работает.

if (bitmap != null)
{
  bitmap.recycle();
  bitmap = null;
}
if (imageView != null)
{
  imageView.setImageResource(R.drawable.tiny); // This is my 1x1 png.
}
System.gc();

imageView.setImageBitmap(...); // Do whatever you need to do to load the image you want.
Майк
источник
похоже, что imageView действительно не перерабатывает растровое изображение само по себе. Помог мне, спасибо
Дмитрий Зайцев
@Mike, можете ли вы добавить полный код imageloader или дать ссылку для загрузки растровых изображений. Если я использую recycle на растровом изображении, все мои списки отображаются, но все элементы отображаются пустыми
TNR
@ Майк, ты можешь сказать, делаю ли я imageView = null перед вызовом в сборщик мусора, хорошо это или нет?
Youddh
@TNR Я думаю, что здесь вам не хватает того, что bitmapв вышеприведенном коде есть предыдущее уже отображаемое изображение, вам нужно переработать его, очистить все ссылки на него, сделать так, чтобы imageViewоно тоже было забыто, установив крошечную замену gc(); и после всего этого: загрузите ваше НОВОЕ изображение bitmapи отобразите его ...в приведенном выше коде.
TWiStErRob
Это не верно. Вы должны всегда очищать содержимое imageView ДО перезапуска растрового изображения (вместо того, пока оно фактически отображается и используется).
FindOut_Quran
20

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

Вот мой класс BitmapHelper который является доказательством OutOfMemoryError :-)

import java.io.File;
import java.io.FileInputStream;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

public class BitmapHelper
{

    //decodes image and scales it to reduce memory consumption
    public static Bitmap decodeFile(File bitmapFile, int requiredWidth, int requiredHeight, boolean quickAndDirty)
    {
        try
        {
            //Decode image size
            BitmapFactory.Options bitmapSizeOptions = new BitmapFactory.Options();
            bitmapSizeOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapSizeOptions);

            // load image using inSampleSize adapted to required image size
            BitmapFactory.Options bitmapDecodeOptions = new BitmapFactory.Options();
            bitmapDecodeOptions.inTempStorage = new byte[16 * 1024];
            bitmapDecodeOptions.inSampleSize = computeInSampleSize(bitmapSizeOptions, requiredWidth, requiredHeight, false);
            bitmapDecodeOptions.inPurgeable = true;
            bitmapDecodeOptions.inDither = !quickAndDirty;
            bitmapDecodeOptions.inPreferredConfig = quickAndDirty ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888;

            Bitmap decodedBitmap = BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapDecodeOptions);

            // scale bitmap to mathc required size (and keep aspect ratio)

            float srcWidth = (float) bitmapDecodeOptions.outWidth;
            float srcHeight = (float) bitmapDecodeOptions.outHeight;

            float dstWidth = (float) requiredWidth;
            float dstHeight = (float) requiredHeight;

            float srcAspectRatio = srcWidth / srcHeight;
            float dstAspectRatio = dstWidth / dstHeight;

            // recycleDecodedBitmap is used to know if we must recycle intermediary 'decodedBitmap'
            // (DO NOT recycle it right away: wait for end of bitmap manipulation process to avoid
            // java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@416ee7d8
            // I do not excatly understand why, but this way it's OK

            boolean recycleDecodedBitmap = false;

            Bitmap scaledBitmap = decodedBitmap;
            if (srcAspectRatio < dstAspectRatio)
            {
                scaledBitmap = getScaledBitmap(decodedBitmap, (int) dstWidth, (int) (srcHeight * (dstWidth / srcWidth)));
                // will recycle recycleDecodedBitmap
                recycleDecodedBitmap = true;
            }
            else if (srcAspectRatio > dstAspectRatio)
            {
                scaledBitmap = getScaledBitmap(decodedBitmap, (int) (srcWidth * (dstHeight / srcHeight)), (int) dstHeight);
                recycleDecodedBitmap = true;
            }

            // crop image to match required image size

            int scaledBitmapWidth = scaledBitmap.getWidth();
            int scaledBitmapHeight = scaledBitmap.getHeight();

            Bitmap croppedBitmap = scaledBitmap;

            if (scaledBitmapWidth > requiredWidth)
            {
                int xOffset = (scaledBitmapWidth - requiredWidth) / 2;
                croppedBitmap = Bitmap.createBitmap(scaledBitmap, xOffset, 0, requiredWidth, requiredHeight);
                scaledBitmap.recycle();
            }
            else if (scaledBitmapHeight > requiredHeight)
            {
                int yOffset = (scaledBitmapHeight - requiredHeight) / 2;
                croppedBitmap = Bitmap.createBitmap(scaledBitmap, 0, yOffset, requiredWidth, requiredHeight);
                scaledBitmap.recycle();
            }

            if (recycleDecodedBitmap)
            {
                decodedBitmap.recycle();
            }
            decodedBitmap = null;

            scaledBitmap = null;
            return croppedBitmap;
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * compute powerOf2 or exact scale to be used as {@link BitmapFactory.Options#inSampleSize} value (for subSampling)
     * 
     * @param requiredWidth
     * @param requiredHeight
     * @param powerOf2
     *            weither we want a power of 2 sclae or not
     * @return
     */
    public static int computeInSampleSize(BitmapFactory.Options options, int dstWidth, int dstHeight, boolean powerOf2)
    {
        int inSampleSize = 1;

        // Raw height and width of image
        final int srcHeight = options.outHeight;
        final int srcWidth = options.outWidth;

        if (powerOf2)
        {
            //Find the correct scale value. It should be the power of 2.

            int tmpWidth = srcWidth, tmpHeight = srcHeight;
            while (true)
            {
                if (tmpWidth / 2 < dstWidth || tmpHeight / 2 < dstHeight)
                    break;
                tmpWidth /= 2;
                tmpHeight /= 2;
                inSampleSize *= 2;
            }
        }
        else
        {
            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) srcHeight / (float) dstHeight);
            final int widthRatio = Math.round((float) srcWidth / (float) dstWidth);

            // Choose the smallest ratio as inSampleSize value, this will guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }

        return inSampleSize;
    }

    public static Bitmap drawableToBitmap(Drawable drawable)
    {
        if (drawable instanceof BitmapDrawable)
        {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    }

    public static Bitmap getScaledBitmap(Bitmap bitmap, int newWidth, int newHeight)
    {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;

        // CREATE A MATRIX FOR THE MANIPULATION
        Matrix matrix = new Matrix();
        // RESIZE THE BIT MAP
        matrix.postScale(scaleWidth, scaleHeight);

        // RECREATE THE NEW BITMAP
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
        return resizedBitmap;
    }

}
Паскаль
источник
Для тех, кто использует это: я только исправил ошибку: "int scaledBitmapHeight = scaledBitmap.getWidth ();" неправильно (очевидно. Я заменил его на «int scaledBitmapHeight = scaledBitmap.getHeight ();»
Паскаль
19

Это работает для меня.

Bitmap myBitmap;

BitmapFactory.Options options = new BitmapFactory.Options(); 
options.InPurgeable = true;
options.OutHeight = 50;
options.OutWidth = 50;
options.InSampleSize = 4;

File imgFile = new File(filepath);
myBitmap = BitmapFactory.DecodeFile(imgFile.AbsolutePath, options);

и это на C # monodroid. Вы можете легко изменить путь изображения. что здесь важно, так это варианты, которые нужно установить.

Dobermaxx99
источник
16

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

package com.emil;

import java.io.IOException;
import java.io.InputStream;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

/**
 * A class to load and process images of various sizes from input streams and file paths.
 * 
 * @author Emil http://stackoverflow.com/users/220710/emil
 *
 */
public class ImageProcessing {

    public static Bitmap getBitmap(InputStream stream, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
        Bitmap bm = BitmapFactory.decodeStream(stream,null,options);
        if(ImageProcessing.checkDecode(options)){
            return bm;
        }else{
            throw new IOException("Image decoding failed, using stream.");
        }
    }

    public static Bitmap getBitmap(String imgPath, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
        Bitmap bm = BitmapFactory.decodeFile(imgPath,options);
        if(ImageProcessing.checkDecode(options)){
            return bm;
        }else{
            throw new IOException("Image decoding failed, using file path.");
        }
    }

    public static Dimensions getDimensions(InputStream stream) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
        BitmapFactory.decodeStream(stream,null,options);
        if(ImageProcessing.checkDecode(options)){
            return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
        }else{
            throw new IOException("Image decoding failed, using stream.");
        }
    }

    public static Dimensions getDimensions(String imgPath) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
        BitmapFactory.decodeFile(imgPath,options);
        if(ImageProcessing.checkDecode(options)){
            return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
        }else{
            throw new IOException("Image decoding failed, using file path.");
        }
    }

    private static boolean checkDecode(BitmapFactory.Options options){
        // Did decode work?
        if( options.outWidth<0 || options.outHeight<0 ){
            return false;
        }else{
            return true;
        }
    }

    /**
     * Creates a Bitmap that is of the minimum dimensions necessary
     * @param bm
     * @param min
     * @return
     */
    public static Bitmap createMinimalBitmap(Bitmap bm, ImageProcessing.Minimize min){
        int newWidth, newHeight;
        switch(min.type){
        case WIDTH:
            if(bm.getWidth()>min.minWidth){
                newWidth=min.minWidth;
                newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
            }else{
                // No resize
                newWidth=bm.getWidth();
                newHeight=bm.getHeight();
            }
            break;
        case HEIGHT:
            if(bm.getHeight()>min.minHeight){
                newHeight=min.minHeight;
                newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
            }else{
                // No resize
                newWidth=bm.getWidth();
                newHeight=bm.getHeight();
            }
            break;
        case BOTH: // minimize to the maximum dimension
        case MAX:
            if(bm.getHeight()>bm.getWidth()){
                // Height needs to minimized
                min.minDim=min.minDim!=null ? min.minDim : min.minHeight;
                if(bm.getHeight()>min.minDim){
                    newHeight=min.minDim;
                    newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
                }else{
                    // No resize
                    newWidth=bm.getWidth();
                    newHeight=bm.getHeight();
                }
            }else{
                // Width needs to be minimized
                min.minDim=min.minDim!=null ? min.minDim : min.minWidth;
                if(bm.getWidth()>min.minDim){
                    newWidth=min.minDim;
                    newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
                }else{
                    // No resize
                    newWidth=bm.getWidth();
                    newHeight=bm.getHeight();
                }
            }
            break;
        default:
            // No resize
            newWidth=bm.getWidth();
            newHeight=bm.getHeight();
        }
        return Bitmap.createScaledBitmap(bm, newWidth, newHeight, true);
    }

    public static int getScaledWidth(int height, Bitmap bm){
        return (int)(((double)bm.getWidth()/bm.getHeight())*height);
    }

    public static int getScaledHeight(int width, Bitmap bm){
        return (int)(((double)bm.getHeight()/bm.getWidth())*width);
    }

    /**
     * Get the proper sample size to meet minimization restraints
     * @param dim
     * @param min
     * @param multipleOf2 for fastest processing it is recommended that the sample size be a multiple of 2
     * @return
     */
    public static int getSampleSize(ImageProcessing.Dimensions dim, ImageProcessing.Minimize min, boolean multipleOf2){
        switch(min.type){
        case WIDTH:
            return ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
        case HEIGHT:
            return ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
        case BOTH:
            int widthMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
            int heightMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
            // Return the smaller of the two
            if(widthMaxSampleSize<heightMaxSampleSize){
                return widthMaxSampleSize;
            }else{
                return heightMaxSampleSize;
            }
        case MAX:
            // Find the larger dimension and go bases on that
            if(dim.width>dim.height){
                return ImageProcessing.getMaxSampleSize(dim.width, min.minDim, multipleOf2);
            }else{
                return ImageProcessing.getMaxSampleSize(dim.height, min.minDim, multipleOf2);
            }
        }
        return 1;
    }

    public static int getMaxSampleSize(int dim, int min, boolean multipleOf2){
        int add=multipleOf2 ? 2 : 1;
        int size=0;
        while(min<(dim/(size+add))){
            size+=add;
        }
        size = size==0 ? 1 : size;
        return size;        
    }

    public static class Dimensions {
        int width;
        int height;

        public Dimensions(int width, int height) {
            super();
            this.width = width;
            this.height = height;
        }

        @Override
        public String toString() {
            return width+" x "+height;
        }
    }

    public static class Minimize {
        public enum Type {
            WIDTH,HEIGHT,BOTH,MAX
        }
        Integer minWidth;
        Integer minHeight;
        Integer minDim;
        Type type;

        public Minimize(int min, Type type) {
            super();
            this.type = type;
            switch(type){
            case WIDTH:
                this.minWidth=min;
                break;
            case HEIGHT:
                this.minHeight=min;
                break;
            case BOTH:
                this.minWidth=min;
                this.minHeight=min;
                break;
            case MAX:
                this.minDim=min;
                break;
            }
        }

        public Minimize(int minWidth, int minHeight) {
            super();
            this.type=Type.BOTH;
            this.minWidth = minWidth;
            this.minHeight = minHeight;
        }

    }

    /**
     * Estimates size of Bitmap in bytes depending on dimensions and Bitmap.Config
     * @param width
     * @param height
     * @param config
     * @return
     */
    public static long estimateBitmapBytes(int width, int height, Bitmap.Config config){
        long pixels=width*height;
        switch(config){
        case ALPHA_8: // 1 byte per pixel
            return pixels;
        case ARGB_4444: // 2 bytes per pixel, but depreciated
            return pixels*2;
        case ARGB_8888: // 4 bytes per pixel
            return pixels*4;
        case RGB_565: // 2 bytes per pixel
            return pixels*2;
        default:
            return pixels;
        }
    }

    private static BitmapFactory.Options getOptionsForDimensions(){
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds=true;
        return options;
    }

    private static BitmapFactory.Options getOptionsForSampling(int sampleSize, Bitmap.Config bitmapConfig){
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inSampleSize = sampleSize;
        options.inScaled = false;
        options.inPreferredConfig = bitmapConfig;
        return options;
    }
}
Эмиль Давтян
источник
16

В одном из моих приложений мне нужно сделать снимок либо с Camera/Gallery. Если пользователь щелкает изображение с камеры (может быть 2MP, 5MP или 8MP), размер изображения варьируется от kBs до MBs. Если размер изображения меньше (или до 1-2 МБ) выше, код работает нормально, но если у меня есть изображение размером более 4 МБ или 5 МБ, тогда OOMпоявляется кадр :(

затем я работал над решением этой проблемы и, наконец, я сделал приведенное ниже усовершенствование кода Федора (Весь кредит Федору за создание такого замечательного решения) :)

private Bitmap decodeFile(String fPath) {
    // Decode image size
    BitmapFactory.Options opts = new BitmapFactory.Options();
    /*
     * If set to true, the decoder will return null (no bitmap), but the
     * out... fields will still be set, allowing the caller to query the
     * bitmap without having to allocate the memory for its pixels.
     */
    opts.inJustDecodeBounds = true;
    opts.inDither = false; // Disable Dithering mode
    opts.inPurgeable = true; // Tell to gc that whether it needs free
                                // memory, the Bitmap can be cleared
    opts.inInputShareable = true; // Which kind of reference will be used to
                                    // recover the Bitmap data after being
                                    // clear, when it will be used in the
                                    // future

    BitmapFactory.decodeFile(fPath, opts);

    // The new size we want to scale to
    final int REQUIRED_SIZE = 70;

    // Find the correct scale value. 
    int scale = 1;

    if (opts.outHeight > REQUIRED_SIZE || opts.outWidth > REQUIRED_SIZE) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) opts.outHeight
                / (float) REQUIRED_SIZE);
        final int widthRatio = Math.round((float) opts.outWidth
                / (float) REQUIRED_SIZE);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        scale = heightRatio < widthRatio ? heightRatio : widthRatio;//
    }

    // Decode bitmap with inSampleSize set
    opts.inJustDecodeBounds = false;

    opts.inSampleSize = scale;

    Bitmap bm = BitmapFactory.decodeFile(fPath, opts).copy(
            Bitmap.Config.RGB_565, false);

    return bm;

}

Я надеюсь, что это поможет друзьям, сталкивающимся с той же проблемой!

для более подробной информации, пожалуйста, обратитесь к этому

Рупеш Ядав
источник
14

Я только столкнулся с этой проблемой пару минут назад. Я решил это, сделав лучшую работу по управлению моим адаптером списка. Я подумал, что это проблема с сотнями изображений размером 50x50 пикселей, которые я использовал. Оказывается, я пытался раздуть свой пользовательский вид при каждом показе строки. Просто проверив, завышена ли строка, я устранил эту ошибку и использую сотни растровых изображений. Это на самом деле для Spinner, но базовый адаптер все равно работает для ListView. Это простое исправление также значительно улучшило производительность адаптера.

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {

    if(convertView == null){
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.spinner_row, null);
    }
...
BajaBob
источник
3
Я не могу отблагодарить тебя за это! Я преследовал не ту проблему, прежде чем увидел это. Вопрос для вас: поскольку каждая или моя строка списка имеют уникальное имя и фотографию, мне пришлось использовать массив convertView, чтобы сохранить значения каждой строки. Я не мог понять, как использование одной переменной позволит вам сделать это. Я что-то пропустил?
PeteH
13

Я потратил целый день на тестирование этих решений, и единственное, что сработало для меня, - это описанные выше подходы для получения изображения и ручного вызова GC, что, как я знаю, не обязательно, но это единственное, что сработало когда я ставлю свое приложение под нагрузочное тестирование, переключаясь между действиями. Мое приложение имеет список миниатюрных изображений в виде списка в (скажем, действие A), и когда вы нажимаете на одно из этих изображений, оно переводит вас в другое действие (например, действие B), которое показывает основное изображение для этого элемента. Когда я переключался между этими двумя действиями, я в итоге получал ошибку OOM, и приложение принудительно закрывалось.

Когда я попаду на полпути вниз по списку, произойдет сбой.

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

@Override
public void onDestroy()
{   
    Cleanup();
    super.onDestroy();
}

private void Cleanup()
{    
    bitmap.recycle();
    System.gc();
    Runtime.getRuntime().gc();  
}
Джесси
источник
Любите ваше решение! Я также часами решаю эту ошибку, так что раздражает! Редактировать: К сожалению, проблема все еще существует, когда я меняю ориентацию экрана в ландшафтном режиме ...
Xarialon
Это, наконец, помогло мне вместе с: - BitmapFactory.Options options = new BitmapFactory.Options (); options.InPurgeable = true; options.InSampleSize = 2;
user3833732
13

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

Поэтому, пожалуйста, проверьте устройство. Это может быть запущено в устройстве.

хакдзюцу
источник
12

Мои 2 цента: я решил свои ошибки OOM с помощью растровых изображений:

а) масштабирование моих изображений в 2 раза

б) использование библиотеки Пикассо в моем настраиваемом адаптере для ListView с помощью одного вызова в getView, например:Picasso.with(context).load(R.id.myImage).into(R.id.myImageView);

matsoftware
источник
Я рад, что вы упомянули Пикассо, потому что это значительно упрощает загрузку изображений. Особенно удаленно хранятся.
Chrispix
12

используйте этот код для каждого изображения, выбранного из SdCard или drewable для преобразования растрового объекта.

Resources res = getResources();
WindowManager window = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = window.getDefaultDisplay();
@SuppressWarnings("deprecation")
int width = display.getWidth();
@SuppressWarnings("deprecation")
int height = display.getHeight();
try {
    if (bitmap != null) {
        bitmap.recycle();
        bitmap = null;
        System.gc();
    }
    bitmap = Bitmap.createScaledBitmap(BitmapFactory
        .decodeFile(ImageData_Path.get(img_pos).getPath()),
        width, height, true);
} catch (OutOfMemoryError e) {
    if (bitmap != null) {
        bitmap.recycle();
        bitmap = null;
        System.gc();
    }
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Config.RGB_565;
    options.inSampleSize = 1;
    options.inPurgeable = true;
    bitmapBitmap.createScaledBitmap(BitmapFactory.decodeFile(ImageData_Path.get(img_pos)
        .getPath().toString(), options), width, height,true);
}
return bitmap;

используйте путь к изображению в ImageData_Path.get (img_pos) .getPath () .

Гаурав Паншерия
источник
12

Как правило, размер кучи устройства Android составляет всего 16 МБ (зависит от устройства / ОС, см. Размеры кучи после публикации ), если вы загружаете изображения и размер их превышает 16 МБ, из-за исключения из памяти будет выброшено исключение, вместо использования битовой карты для загрузки. изображения с SD-карты или из ресурсов или даже из сети пытаются использовать getImageUri , загрузка растрового изображения требует больше памяти, или вы можете установить для растрового изображения значение null, если ваша работа выполняется с этим растровым изображением.

Rohit Sharma
источник
1
И если setImageURI все еще получает исключение, тогда обратитесь к этому stackoverflow.com/questions/15377186/…
Махеш
11

Все решения здесь требуют установки IMAGE_MAX_SIZE. Это ограничивает устройства более мощным аппаратным обеспечением, и если размер изображения слишком мал, на экране HD он выглядит ужасно.

Я предложил решение, которое работает с моим Samsung Galaxy S3 и несколькими другими устройствами, включая менее мощные, с лучшим качеством изображения при использовании более мощного устройства.

Суть его заключается в том, чтобы рассчитать максимальный объем памяти, выделенный для приложения на конкретном устройстве, а затем установить минимально возможный масштаб без превышения этой памяти. Вот код:

public static Bitmap decodeFile(File f)
{
    Bitmap b = null;
    try
    {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;

        FileInputStream fis = new FileInputStream(f);
        try
        {
            BitmapFactory.decodeStream(fis, null, o);
        }
        finally
        {
            fis.close();
        }

        // In Samsung Galaxy S3, typically max memory is 64mb
        // Camera max resolution is 3264 x 2448, times 4 to get Bitmap memory of 30.5mb for one bitmap
        // If we use scale of 2, resolution will be halved, 1632 x 1224 and x 4 to get Bitmap memory of 7.62mb
        // We try use 25% memory which equals to 16mb maximum for one bitmap
        long maxMemory = Runtime.getRuntime().maxMemory();
        int maxMemoryForImage = (int) (maxMemory / 100 * 25);

        // Refer to
        // http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
        // A full screen GridView filled with images on a device with
        // 800x480 resolution would use around 1.5MB (800*480*4 bytes)
        // When bitmap option's inSampleSize doubled, pixel height and
        // weight both reduce in half
        int scale = 1;
        while ((o.outWidth / scale) * (o.outHeight / scale) * 4 > maxMemoryForImage)
        scale *= 2;

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        fis = new FileInputStream(f);
        try
        {
            b = BitmapFactory.decodeStream(fis, null, o2);
        }
        finally
        {
            fis.close();
        }
    }
    catch (IOException e)
    {
    }
    return b;
}

Я установил, что максимальный объем памяти, используемой этим растровым изображением, составляет 25% от максимально выделенного объема памяти. Возможно, вам придется настроить его в соответствии с вашими потребностями и убедиться, что этот растровый рисунок очищен и не остается в памяти после его использования. Обычно я использую этот код для поворота изображения (исходное и конечное растровое изображение), поэтому моему приложению необходимо одновременно загружать 2 растровых изображения в память, и 25% дают мне хороший буфер без исчерпания памяти при выполнении поворота изображения.

Надеюсь, это поможет кому-то там ..

Брюс
источник
11

Такое OutofMemoryExceptionне может быть полностью решено путем вызова System.gc()и так далее.

Ссылаясь на жизненный цикл деятельности

Состояния активности определяются самой ОС с учетом использования памяти для каждого процесса и приоритета каждого процесса.

Вы можете рассмотреть размер и разрешение для каждого из используемых растровых изображений. Рекомендую уменьшить размер, изменить выборку до более низкого разрешения, обратиться к дизайну галерей (одно маленькое изображение PNG и одно оригинальное изображение).

Раджу Гуджарати
источник
11

Этот код поможет загрузить большое растровое изображение из нарисованного

public class BitmapUtilsTask extends AsyncTask<Object, Void, Bitmap> {

    Context context;

    public BitmapUtilsTask(Context context) {
        this.context = context;
    }

    /**
     * Loads a bitmap from the specified url.
     * 
     * @param url The location of the bitmap asset
     * @return The bitmap, or null if it could not be loaded
     * @throws IOException
     * @throws MalformedURLException
     */
    public Bitmap getBitmap() throws MalformedURLException, IOException {       

        // Get the source image's dimensions
        int desiredWidth = 1000;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;

        BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);

        int srcWidth = options.outWidth;
        int srcHeight = options.outHeight;

        // Only scale if the source is big enough. This code is just trying
        // to fit a image into a certain width.
        if (desiredWidth > srcWidth)
            desiredWidth = srcWidth;

        // Calculate the correct inSampleSize/scale value. This helps reduce
        // memory use. It should be a power of 2
        int inSampleSize = 1;
        while (srcWidth / 2 > desiredWidth) {
            srcWidth /= 2;
            srcHeight /= 2;
            inSampleSize *= 2;
        }
        // Decode with inSampleSize
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inSampleSize = inSampleSize;
        options.inScaled = false;
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        options.inPurgeable = true;
        Bitmap sampledSrcBitmap;

        sampledSrcBitmap =  BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);

        return sampledSrcBitmap;
    }

    /**
     * The system calls this to perform work in a worker thread and delivers
     * it the parameters given to AsyncTask.execute()
     */
    @Override
    protected Bitmap doInBackground(Object... item) {
        try { 
          return getBitmap();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
vineet
источник
Хорошо, думаю, что было бы лучше использовать Loader вместо Async Task?
Chrispix
Как насчет Bitmap.Config.ARGB_565? Если высокое качество не критично.
Хамзех Собох