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

1166

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

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

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

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

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

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

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

FYI: Вот как я это делал:

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
  • 15
    Я положил журнал в блок кода, чтобы дать ему полосы прокрутки
  • 7
    Я решил это, избегая Bitmap.decodeStream или decodeFile и используя метод BitmapFactory.decodeFileDescriptor.
Показать ещё 6 комментариев
Теги:
image
android-bitmap
out-of-memory
bitmap

41 ответ

590
Лучший ответ

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


Считать размеры и тип растрового изображения

Класс 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. Например, изображение с разрешением 2048 × 1536, декодированное с помощью inSampleSize of 4, создает растровое изображение приблизительно 512x384. Загрузка этого в память использует 0,75 МБ, а не 12 МБ для полного изображения (при условии конфигурации растрового изображения ARGB_8888). Heres метод для вычисления значения размера выборки, который является мощностью двух на основе ширины и высоты цели:

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* по мере необходимости.

  • 19
    Этот ответ обсуждается на мета
  • 7
    Этот ответ (кроме информации, полученной по ссылке) не предлагает решения, как для ответа. Важные части ссылки должны быть объединены в вопросе.
Показать ещё 1 комментарий
817

Чтобы исправить ошибку 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;
}
  • 29
    Обратите внимание, что 10 может быть не лучшим значением для inSampleSize, хотя в документации предлагается использовать степень 2.
  • 65
    Я сталкиваюсь с той же проблемой, что и Chrispix, но я не думаю, что решение здесь действительно решает проблему, а скорее обходит ее. Изменение размера выборки уменьшает объем используемой памяти (за счет качества изображения, которое, вероятно, подходит для предварительного просмотра изображения), но это не предотвратит исключение, если декодируется достаточно большой поток изображений, если несколько потоков изображений декодируется. Если я найду лучшее решение (а может и не найти), я выложу ответ здесь.
Показать ещё 21 комментарий
371

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

 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;
}
  • 38
    Да ты прав пока не так красиво. Я просто пытался объяснить это всем. Спасибо за ваш код.
  • 9
    @Tommas Vervest - есть большая проблема с этим кодом. ^ не возводит 2 в степень, это xors 2 с результатом. Вы хотите Math.pow (2.0, ...). В противном случае это выглядит хорошо.
Показать ещё 26 комментариев
232

Я пришел из опыта 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.

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

  • 21
    BitmapFactory.Options options = new BitmapFactory.Options(); options.inPurgeable = true; и Bitmap.createScaledBitmap(srcBitmap, width, height, false); решил мою проблему с исключением из-за нехватки памяти на Android 4.0.0. Спасибо друг!
  • 4
    В вызове Bitmap.createScaledBitmap () вы, вероятно, должны использовать true в качестве параметра флага. В противном случае качество изображения не будет плавным при увеличении. Проверьте эту ветку stackoverflow.com/questions/2895065/…
Показать ещё 7 комментариев
81

Это известная ошибка, это не из-за больших файлов. Так как 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);
}
67

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

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

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

Я расскажу о версиях SDK раньше, чем Honey Comb.

  • Растровое изображение сохраняется в нативной куче, но оно автоматически собирает мусор, поэтому вызов recycle() бесполезен.

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

    УВЕДОМЛЕНИЕ: РАЗМЕР ВНУТРЕННЕГО ВЕЩЕСТВА засчитывается, а не виртуальная память.

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

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

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

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

Затем поговорим о SDK, начиная с Honey Comb.

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

  • Условие для OOM намного проще: {VM heap size} >= {Ограничение размера кучи VM для устройства}.

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

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

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

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

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

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

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

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

У меня была эта же проблема и я решил ее, избегая функций 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, а через собственные вызовы, выделения выполняются за пределами виртуальной кучи, но подсчитано против! "

  • 0
    тот же результат из памяти, на самом деле не имеет значения, какой метод вы используете, зависит от количества байтов, которые вы держите для чтения данных, которые выдают из памяти.
  • 0
    Что если путь - это URL?
61

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

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

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

Требования:

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

Изображение 2811

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

  • Сохраняет кеш, если есть изменение ориентации, используя singleton
  • Используйте одну восьмую из назначенной памяти приложения в кеше (измените, если хотите)
  • Большие битовые карты масштабируются (вы можете определить максимальные пиксели, которые вы хотите разрешить)
  • Элементы управления доступом к Интернету доступны перед загрузкой растровых изображений.
  • Удостоверяется, что вы создаете только одну задачу за строку
  • Если вы отбрасываете 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 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 появится, и если мы загрузим много, действительно больших изображений, и до того, как они будут масштабированы и помещены в кеш, будут одновременно занимать больше памяти и вызывать ООМ. Но это даже не идеальная ситуация, и ее, скорее всего, не удастся решить более осуществимым способом.

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

36

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

Bitmap bm;
bm = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(filepath), 100, 100, true);
mPicture = new ImageView(context);
mPicture.setImageBitmap(bm);    
  • 23
    Этот подход масштабирует растровое изображение. Но это не решает проблему OutOfMemory, потому что в любом случае декодируется полное растровое изображение.
  • 4
    Я посмотрю, смогу ли я посмотреть на свой старый код, но думаю, что он решил мои проблемы с нехваткой памяти. Буду перепроверять мой старый код.
Показать ещё 2 комментария
32

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

 <application
         android:largeHeap="true"
  • 0
    Можете ли вы объяснить, что это на самом деле делает? Просто сказать людям, чтобы добавить это не помогает.
  • 0
    Это очень плохое решение. По сути, вы не пытаетесь решить проблему. Вместо этого попросите систему Android выделить больше пространства кучи для вашего приложения. Это будет иметь очень плохие последствия для вашего приложения, например, для вашего приложения, потребляющего много энергии батареи, поскольку GC должен пробежать через большое пространство кучи для очистки памяти, а также производительность вашего приложения будет ниже.
Показать ещё 1 комментарий
31

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

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

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

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

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

  • 0
    inPurgeable исправил OOM для меня.
27

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

  • 7
    Согласно API, вызов recycle () не нужен.
  • 0
    Да, это неправильно. Полное решение .. stackoverflow.com/a/24135283/294884
26

Скорее всего, вы страдаете от этой ошибки

Отчет об ошибке содержит тестовый файл.

26

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

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);
  • 0
    Это все правильно, но я использую несколько растровых изображений для рисования круга в OnCreate и вызове действия 4-5 раз, так как очистить растровое изображение и как удалить растровое изображение и создать заново во второй раз, когда действие 0nCreate ..
24

Здесь есть две проблемы.

  • Растровая память не находится в куче VM, а скорее в нативной куче - см. BitmapFactory OOM заставляет меня гадать
  • Сбор мусора для родной кучи более ленив, чем куча VM, поэтому вам нужно быть достаточно агрессивным в том, чтобы делать bitmap.recycle и bitmap = null каждый раз, когда вы проходите через Activity onPause или onDestroy
  • 0
    Именно в куче ВМ начиная с андроида 2.3+
23

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

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 value is null");
        } else {
            return bitmap;
        }
    } catch (IOException e) {
        throw new IOException("File cannot be opened: " + e.getMessage());
    }
}
23

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

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

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

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

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

  • 4
    "и затем кешировать его на карте против его имени." Как именно вы кэшируете свои изображения?
  • 3
    Вы действительно пробовали это? Несмотря на то, что данные о пикселях на самом деле не хранятся в куче Dalvik, их размер в собственной памяти передается на виртуальную машину и учитывается в ее доступной памяти.
Показать ещё 2 комментария
19

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

Вот мой класс 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 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;
    }

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

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

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);

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

18

Ни один из вышеперечисленных ответов не работал у меня, но я придумал ужасно уродливое обходное решение, которое решило проблему. Я добавил очень маленький, 1x1 пиксельный образ в свой проект в качестве ресурса и загрузил его в свой ImageView перед тем, как позвонить в сборку мусора. Я думаю, возможно, что ImageView не выпустил Bitmap, поэтому 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.
  • 0
    похоже, что imageView действительно не перерабатывает растровое изображение само по себе. Помог мне, спасибо
  • 0
    @Mike, можете ли вы добавить полный код imageloader или дать ссылку для загрузки растровых изображений. Если я использую recycle на растровом изображении, все мои списки отображаются, но все элементы отображаются пустыми
Показать ещё 3 комментария
14

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

то я работал над решением этой проблемы, и, наконец, я сделал следующее усовершенствование для кода Fedor's (All Credit to Fedor для создания такого приятного решения):)

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;

}

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

для более подробной информации см. this

14

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

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;
    }
}
13

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

Итак, проверьте устройство. Его можно запустить на устройстве.

  • 6
    Так что не правда, это происходит на многих устройствах Samsung ...
13

Я столкнулся с этим вопросом пару минут назад. Я решил это, сделав лучшую работу по управлению моим адаптером listview. Я думал, что это проблема с сотнями 50х50 пикселей изображений, которые я использовал, оказывается, я пытался раздувать свое пользовательское представление каждый раз, когда показывалась строка. Просто тестируя, чтобы увидеть, была ли строка раздута, я устранил эту ошибку, и я использую сотни растровых изображений. Это фактически для 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);
    }
...
  • 3
    Я не могу отблагодарить тебя за это! Я преследовал не ту проблему, прежде чем увидел это. Вопрос для вас: поскольку каждая или моя строка списка имеют уникальное имя и фотографию, мне пришлось использовать массив convertView, чтобы сохранить значения каждой строки. Я не мог понять, как использование одной переменной позволит вам сделать это. Я что-то пропустил?
12

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

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

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

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

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

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

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().

11

Мои 2 цента: я решил ошибки OOM с растровыми изображениями:

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

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

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

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

  • 0
    И если setImageURI все еще получает исключение, тогда обратитесь к этому stackoverflow.com/questions/15377186/…
10

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

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 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;
 }

}

  • 0
    Хорошо, думаю, что было бы лучше использовать Loader вместо Async Task?
  • 0
    Как насчет Bitmap.Config.ARGB_565 ? Если высокое качество не критично.
10

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

Обратившись к жизненному циклу активности

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

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

9

Все решения здесь требуют установки 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 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% дает мне хороший буфер без нехватки памяти при выполнении вращения изображения.

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

  • 0
    +1 за просмотр доступной памяти
5

Похоже, что изображения, которые вы использовали, очень велики по размеру. Некоторые из ранних сбоев устройств происходят из-за полной памяти кучи. В более старых устройствах (медовый гребень или ICS или любые устройства с низкой ценой) попробуйте использовать android:largeHeap="true" в файл манифеста под тегом приложения или уменьшить размер растрового изображения, используя приведенный ниже код.

Bitmap bMap;
BitmapFactory.Options options = new BitmapFactory.Options(); 
options.InSampleSize = 8;
bMap= BitmapFactory.DecodeFile(imgFile.AbsolutePath, options);

вы также можете дать 4 или 12 или 16, чтобы уменьшить размер растрового изображения

5
BitmapFactory.Options options = new Options();
options.inSampleSize = 32;
//img = BitmapFactory.decodeFile(imageids[position], options);

Bitmap theImage = BitmapFactory.decodeStream(imageStream,null, options);
Bitmap img=theImage.copy(Bitmap.Config.RGB_565,true);
theImage.recycle();
theImage = null;
System.gc();
//ivlogdp.setImageBitmap(img);
Runtime.getRuntime().gc();
  • 0
    Вызов .gc бесполезен. Это просто намекает компилятору, что сейчас хорошее время для выполнения GC, но нет никакой гарантии, что это действительно произойдет в данный момент.
4

Я попробовал подход Томаса Вервеста, но он возвращает масштаб 1 для размера изображения 2592x1944, когда IMAGE_MAX_SIZE равно 2048.

Эта версия работала для меня на основе всех других комментариев, предоставленных другими:

private 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 ();
        }

        int scale = 1;
        for (int size = Math.max (o.outHeight, o.outWidth); 
            (size>>(scale-1)) > IMAGE_MAX_SIZE; ++scale);

        // 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;
}
2

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

public Bitmap loadBitmap(String URL, BitmapFactory.Options options) {
                Bitmap bitmap = null;
                InputStream in = null;
                options.inSampleSize=4;
                try {
                    in = OpenHttpConnection(URL);
                    Log.e("In====>", in+"");
                    bitmap = BitmapFactory.decodeStream(in, null, options);
                    Log.e("URL====>", bitmap+"");

                    in.close();
                } catch (IOException e1) {
                }
                return bitmap;
            }

и

try {
                    BitmapFactory.Options bmOptions;
                    bmOptions = new BitmapFactory.Options();
                    bmOptions.inSampleSize = 1;
                    if(studentImage != null){
                        galleryThumbnail= loadBitmap(IMAGE_URL+studentImage, bmOptions);    
                    }

                    galleryThumbnail=getResizedBitmap(galleryThumbnail, imgEditStudentPhoto.getHeight(), imgEditStudentPhoto.getWidth());
                    Log.e("Image_Url==>",IMAGE_URL+studentImage+"");

                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
  • 0
    Я думаю, что вы упускаете, чтобы поместить код метода getResizedBitmap () !!!
2

Привет, пожалуйста, перейдите по ссылке http://developer.android.com/training/displaying-bitmaps/index.html

или просто попытайтесь извлечь растровое изображение с заданной функцией

private Bitmap decodeBitmapFile (File f) {
    Bitmap bitmap = 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 ();
        }

        int scale = 1;
        for (int size = Math.max (o.outHeight, o.outWidth); 
            (size>>(scale-1)) > IMAGE_MAX_SIZE; ++scale);

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

используйте эту концепцию, это поможет вам, после этого установите изображение на карте изображения

public static Bitmap convertBitmap(String path)   {

        Bitmap bitmap=null;
        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) {
            e.printStackTrace();
        }

        try {
            if(fs!=null)
            {
                bitmap=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
            }
            } catch (IOException e) {

            e.printStackTrace();
        } finally{ 
            if(fs!=null) {
                try {
                    fs.close();
                } catch (IOException e) {

                    e.printStackTrace();
                }
            }
        }

        return bitmap;
    }

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

public static Bitmap decodeSampledBitmapFromPath(String path, int reqWidth,
            int reqHeight) {

        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);

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

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        Bitmap bmp = BitmapFactory.decodeFile(path, options);
        return bmp;
        }

    public static int calculateInSampleSize(BitmapFactory.Options options,
            int reqWidth, int reqHeight) {

        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {
            if (width > height) {
                inSampleSize = Math.round((float) height / (float) reqHeight);
            } else {
                inSampleSize = Math.round((float) width / (float) reqWidth);
             }
         }
         return inSampleSize;
        }

Надеюсь, это вам очень поможет.

Вы можете получить помощь от сайта разработчика Здесь

0

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

     Glide.with(this).load(yourImageResource).into(imageview);

Вы можете получить репозиторий здесь: https://github.com/bumptech/glide

  • 0
    Он не обрабатывает все сценарии. Glide не является универсальным решением, мы используем Glide, но все еще сталкиваемся со многими сбоями OOM
  • 0
    Я не говорил, что это было универсальное решение. Я добавил его на панель инструментов для тех, кто еще не слышал об этом.
0

Я использовал Decode File Descriptor, который работал у меня:

 FileInputStream  fileInputStream = null;
        try {
            fileInputStream  = new FileInputStream(file);
             FileDescriptor fd = fileInputStream.getFD();
            Bitmap imageBitmap = decodeSampledBitmapFromDescriptor(fd , 612,
                    816);
            imageView.setImageBitmap(imageBitmap);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(fileInputStream != null){
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

Код для декодирования сэмплированного растрового изображения из дескриптора файла:

 /**
     * Decode and sample down a bitmap from a file input stream to the requested width and height.
     *
     * @param fileDescriptor The file descriptor to read from
     * @param reqWidth       The requested width of the resulting bitmap
     * @param reqHeight      The requested height of the resulting bitmap
     * @return A bitmap sampled down from the original with the same aspect ratio and dimensions
     * that are equal to or greater than the requested width and height
     */
    public static Bitmap decodeSampledBitmapFromDescriptor(
            FileDescriptor fileDescriptor, int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);

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

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
    }

    /**
     * Calculate an inSampleSize for use in a {@link android.graphics.BitmapFactory.Options} object when decoding
     * bitmaps using the decode* methods from {@link android.graphics.BitmapFactory}. This implementation calculates
     * the closest inSampleSize that will result in the final decoded bitmap having a width and
     * height equal to or larger than the requested width and height. This implementation does not
     * ensure a power of 2 is returned for inSampleSize which can be faster when decoding but
     * results in a larger bitmap which isn't as useful for caching purposes.
     *
     * @param options   An options object with out* params already populated (run through a decode*
     *                  method with inJustDecodeBounds==true
     * @param reqWidth  The requested width of the resulting bitmap
     * @param reqHeight The requested height of the resulting bitmap
     * @return The value to be used for inSampleSize
     */
    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) {

            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);

            // 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;

            // This offers some additional logic in case the image has a strange
            // aspect ratio. For example, a panorama may have a much larger
            // width than height. In these cases the total pixels might still
            // end up being too large to fit comfortably in memory, so we should
            // be more aggressive with sample down the image (=larger inSampleSize).

            final float totalPixels = width * height;

            // Anything more than 2x the requested pixels we'll sample down further
            final float totalReqPixelsCap = reqWidth * reqHeight * 2;

            while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
                inSampleSize++;
            }
        }
        return inSampleSize;
    }
0

Если ты ленив, как я. вы можете начать использовать библиотеку Picasso для загрузки изображений. http://square.github.io/picasso/

Picasso.with(context).load(R.drawable.landing_screen).into(imageView1); Picasso.with(context).load("file:///android_asset/DvpvklR.png").into(imageView2); Picasso.with(context).load(new File(...)).into(imageView3);

-9

После установки растрового изображения в режим просмотра изображения переработайте его следующим образом:

bitmap.recycle();
bitmap=null;
  • 0
    Recycle пока отображается ???

Ещё вопросы

Сообщество Overcoder
Наверх
Меню