Intereting Posts
Как сделать уведомление возобновлением и не воссоздать деятельность? Может ли приложение Android напрямую подключаться к онлайн-базе данных mysql Можно ли рисовать с сглаживанием на холсте? Андроиды выбирают изображения из галереи Android facebook applicationId не может быть null Анимация высоты контейнера LinearLayout с помощью ValueAnimator CyanogenMod на Android эмулятор – это возможно? Приложение Google Messenger не прикрепляет изображение при отправке MMS Выход из приложения при нажатии кнопки в телефоне телефона Android? Как сообщить своему пользовательскому FragmentPagerAdapter прекращение уничтожения моих фрагментов? Данные не синхронизированы между пользовательским CursorLoader и CursorAdapter, поддерживающим ListView Как установить пользовательский шрифт в заголовке ActionBar? Android – воспроизведение звука при нажатии кнопки – исключение нулевого указателя Не удалось выполнить dex: java.nio.BufferOverflowException. Проверьте журнал Eclipse для отслеживания стека Невидимая / прозрачная кнопка, которая работает как обычный в андроиде?

BitmapFactory.decodeStream вне памяти, несмотря на использование уменьшенного размера выборки

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

Вот мой код:

public static Bitmap decodeSampledBitmapFromResource(InputStream inputStream, int reqWidth, int reqHeight) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; try { while ((len = inputStream.read(buffer)) > -1) { baos.write(buffer, 0, len); } baos.flush(); InputStream is1 = new ByteArrayInputStream(baos.toByteArray()); InputStream is2 = new ByteArrayInputStream(baos.toByteArray()); final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(is1, null, options); options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inPurgeable = true; options.inInputShareable = true; options.inJustDecodeBounds = false; options.inPreferredConfig = Bitmap.Config.ARGB_8888; return BitmapFactory.decodeStream(is2, null, options); } catch (Exception e) { e.printStackTrace(); return null; } } 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; } return inSampleSize; } bitmap = decodeSampledBitmapFromResource(inputStream, 600, 600); 

Я получаю сообщение «Ошибка вне памяти при распределении по 3250016 байт» в этой строке:

 return BitmapFactory.decodeStream(is2, null, options); 

Мне кажется, что 3,2 МБ достаточно мало, чтобы выделяться. Где я иду не так? Как я могу это решить?

РЕДАКТИРОВАТЬ

После изучения этого решения ЗДЕСЬ от N-Joy он отлично работает с Required size 300, но мой размер – 800, так что я все еще получаю ошибку.

Solutions Collecting From Web of "BitmapFactory.decodeStream вне памяти, несмотря на использование уменьшенного размера выборки"

Метод decodeSampledBitmapFromResource не эффективен с точки зрения памяти, поскольку он использует 3 потока: байт ByteArrayOutputStream, ByteArrayInputStream is1 и ByteArrayInputStream is2, каждый из которых хранит одни и те же данные потока изображения (по одному байтовому массиву для каждого).

И когда я тестирую свое устройство (LG nexus 4), чтобы декодировать изображение 2560×1600 на SD-карте до размера 800, требуется следующее:

 03-13 15:47:52.557: E/DecodeBitmap(11177): dalvikPss (beginning) = 1780 03-13 15:47:53.157: E/DecodeBitmap(11177): dalvikPss (decoding) = 26393 03-13 15:47:53.548: E/DecodeBitmap(11177): dalvikPss (after all) = 30401 time = 999 

Мы можем видеть: слишком много выделенной памяти (28,5 МБ) просто для декодирования 4096000 пиксельных изображений.

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

 public Bitmap decodeSampledBitmapFromResourceMemOpt( InputStream inputStream, int reqWidth, int reqHeight) { byte[] byteArr = new byte[0]; byte[] buffer = new byte[1024]; int len; int count = 0; try { while ((len = inputStream.read(buffer)) > -1) { if (len != 0) { if (count + len > byteArr.length) { byte[] newbuf = new byte[(count + len) * 2]; System.arraycopy(byteArr, 0, newbuf, 0, count); byteArr = newbuf; } System.arraycopy(buffer, 0, byteArr, count, len); count += len; } } final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeByteArray(byteArr, 0, count, options); options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inPurgeable = true; options.inInputShareable = true; options.inJustDecodeBounds = false; options.inPreferredConfig = Bitmap.Config.ARGB_8888; int[] pids = { android.os.Process.myPid() }; MemoryInfo myMemInfo = mAM.getProcessMemoryInfo(pids)[0]; Log.e(TAG, "dalvikPss (decoding) = " + myMemInfo.dalvikPss); return BitmapFactory.decodeByteArray(byteArr, 0, count, options); } catch (Exception e) { e.printStackTrace(); return null; } } 

Метод, который выполняет расчет:

 public void onButtonClicked(View v) { int[] pids = { android.os.Process.myPid() }; MemoryInfo myMemInfo = mAM.getProcessMemoryInfo(pids)[0]; Log.e(TAG, "dalvikPss (beginning) = " + myMemInfo.dalvikPss); long startTime = System.currentTimeMillis(); FileInputStream inputStream; String filePath = Environment.getExternalStorageDirectory() .getAbsolutePath() + "/test2.png"; File file = new File(filePath); try { inputStream = new FileInputStream(file); // mBitmap = decodeSampledBitmapFromResource(inputStream, 800, 800); mBitmap = decodeSampledBitmapFromResourceMemOpt(inputStream, 800, 800); ImageView imageView = (ImageView) findViewById(R.id.image); imageView.setImageBitmap(mBitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } myMemInfo = mAM.getProcessMemoryInfo(pids)[0]; Log.e(TAG, "dalvikPss (after all) = " + myMemInfo.dalvikPss + " time = " + (System.currentTimeMillis() - startTime)); } 

И результат:

 03-13 16:02:20.373: E/DecodeBitmap(13663): dalvikPss (beginning) = 1823 03-13 16:02:20.923: E/DecodeBitmap(13663): dalvikPss (decoding) = 18414 03-13 16:02:21.294: E/DecodeBitmap(13663): dalvikPss (after all) = 18414 time = 917 

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

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

смузи-библиотека

Android-BitmapCache

Решение Android-Universal-Image-Loader для OutOfMemoryError: размер растрового изображения превышает бюджет VM

ARGB_8888 использует больше памяти, так как требует значения цвета Alpha, поэтому мое предложение – использовать RGB_565 как указано ЗДЕСЬ

Примечание. Качество будет немного низким по сравнению с ARGB_8888 .

Вероятно, вы придерживаетесь предыдущих растровых ссылок. Я предполагаю, что вы выполняете этот код несколько раз и никогда не выполняете bitmap.recycle() . Память неизбежно закончится.

У меня было много проблем с использованием памяти Bitmap.

Результаты:

  • Большинство устройств имеют ограниченную память кучи для графики, большинство небольших устройств ограничено 16 МБ для общих приложений, а не только для вашего приложения
  • Используйте 4-битные или 8-битные или 16-битные растровые изображения, если это применимо
  • Попробуйте нарисовать фигуры с нуля, опустите растровые изображения, если это возможно.

Используйте WebView для динамической загрузки как можно большего количества изображений, он построен с использованием NDK (низкий уровень), поэтому не имеет ограничений памяти кучи GDI. Он работает плавно и быстро 🙂

Проблемы с памятью при декодировании растровых изображений часто не связаны с размером изображения, которое вы декодируете. Конечно, если вы попытаетесь открыть изображение 5000x5000px, вы потерпите неудачу с OutOfMemoryError, но с размером 800×800 пикселей это будет вполне разумно и должно работать нормально.

Если на вашем устройстве недостаточно памяти с изображением 3,2 МБ, это, скорее всего, потому, что вы пропускаете контекст где-то в приложении.

Это первая часть этого сообщения :

Я думаю, проблема не в вашем макете, проблема в другом месте вашего кода. И, возможно, вы где-то протекаете .

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

Как сказал Рагхунандан, вам нужно будет использовать MAT для поиска активности Activity / Component и устранения утечки контекста.

Лучший способ найти утечку контекста – изменение ориентации. Например, несколько раз вращайте ActivityMain, запустите MAT и проверьте, есть ли у вас только один экземпляр ActivityMain. Если у вас несколько (столько же, сколько и изменение вращения), это означает, что существует утечка контекста.

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

Другие сообщения об утечках памяти:

Android – утечка памяти или?

Ошибка вне памяти в эмуляторе Android, но не на устройстве

Посмотрите это видео. http://www.youtube.com/watch?v=_CruQY55HOk . Не используйте system.gc (), как указано в видео. Используйте анализатор MAT для определения утечек памяти. Возвращаемое растровое изображение слишком велико, что, по-моему, вызывает утечку памяти.

Кажется, у вас есть большое изображение для отображения.

Вы можете загрузить изображение и сохранить на sdcard ( пример ), тогда вы можете использовать этот код для отображения изображения с SD-карты.

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

 private Bitmap decodeFile(FileInputStream f) { try { //decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeStream(f,null,o); //Find the correct scale value. It should be the power of 2. final int REQUIRED_SIZE=70; int width_tmp=o.outWidth, height_tmp=o.outHeight; int scale=1; while(true) { if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE) break; width_tmp/=2; height_tmp/=2; scale*=2; } //decode with inSampleSize BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize=scale; return BitmapFactory.decodeStream(f, null, o2); } catch (FileNotFoundException e) {} return null; } в private Bitmap decodeFile(FileInputStream f) { try { //decode image size BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeStream(f,null,o); //Find the correct scale value. It should be the power of 2. final int REQUIRED_SIZE=70; int width_tmp=o.outWidth, height_tmp=o.outHeight; int scale=1; while(true) { if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE) break; width_tmp/=2; height_tmp/=2; scale*=2; } //decode with inSampleSize BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize=scale; return BitmapFactory.decodeStream(f, null, o2); } catch (FileNotFoundException e) {} return null; } 

И ссылаются на ошибку утечки памяти Android и ошибку при загрузке изображений в gridview android