13

У меня есть изображение с высоким разрешением (2588 * 1603) в выделенной папке. Если я использую ниже код (1), чтобы установить его для ImageView я не получаю исключение OOM и изображения, присвоенного как ожидалось:Почему я НЕ получаю исключение из памяти?

public class MainActivity extends ActionBarActivity{ 


    private ImageView mImageView; 

    int mImageHeight = 0; 
    int mImageWidth = 0; 


    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     mImageView = (ImageView) findViewById(R.id.imageView); 
     mImageView.setScaleType(ScaleType.FIT_CENTER); 

     BitmapFactory.Options sizeOption = new BitmapFactory.Options(); 
     sizeOption.inJustDecodeBounds = true; 
     BitmapFactory.decodeResource(getResources(), R.drawable.a, sizeOption); 
     mImageHeight = sizeOption.outHeight; 
     mImageWidth = sizeOption.outWidth; 

     mImageView.post(new Runnable() { 
      @Override 
      public void run() { 
       try { 
       BitmapRegionDecoder bmpDecoder = BitmapRegionDecoder 
          .newInstance(getResources().openRawResource(R.drawable.a),true); 
      Rect rect = new Rect(0,0,mImageWidth, mImageHeight); 
      BitmapFactory.Options options = new BitmapFactory.Options(); 
      options.inPreferredConfig = Bitmap.Config.ARGB_8888; 
      options.inDensity = getResources().getDisplayMetrics().densityDpi; 
      Bitmap bmp = bmpDecoder.decodeRegion(rect, options); 

      mImageView.setImageBitmap(bmp); 

      } catch (NotFoundException e) { 
       e.printStackTrace(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
      } 
     }); 

    } 
} 

Обратите внимание, что размер Прямоугольник точно так же, как размер изображения.

Но если я использую другие методы, например, 2 или 3, я получаю OOM.

2) mImageView.setBackgroundResource(R.drawable.a); 

    3) Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.a); 
    mImageView.setImageBitmap(bmp); 

В чем разница между 1 и 2,3?

(я знаю, как решить OOM, я просто хочу знать разницу)

+0

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

+0

@Blackbelt да. Я вижу весь битмап как дозу ScaleType.FIT_CENTER с обычным «Bitmap». – mmlooloo

+0

, то, вероятно, 'decodeRegion' игнорирует плотность экрана – Blackbelt

ответ

0

Слишком много деталей картины приводит к памяти.

резюме: 1 использовать масштабированное растровое изображение; 2,3 загрузите полный подробный чертеж (это приведет к нехватке памяти), затем измените размер и установите его в режим просмотра изображений.

Bitmap bmp = bmpDecoder.decodeRegion(rect, options); 

the constructor(InputStream is, boolean isShareable) use the stream, которые не исчерпает память.

Использование BitmapFactory.Options и BitmapRegionDecoder уменьшит растровое изображение.

См: BitmapRegionDecoder will draw its requested content into the Bitmap provided, clipping if the output content size (post scaling) is larger than the provided Bitmap. The provided Bitmap's width, height, and Bitmap.Config will not be changed

2,3

Drawable d = mContext.getDrawable(mResource); 
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.a); 

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

К сожалению для английского языка.

Возможно, вам помогут.

+0

посмотрите на это предложение в моем посте: ** Обратите внимание, что rect размер точно такой же, как размер изображения. ** – mmlooloo

+0

«1 использовать масштабированное растровое изображение» нет никакой масштабированной опции. – mmlooloo

+0

@mmlooloo Я полагаю, что options.inDensity - это масштабированная опция: «Это может быть из-за того, что внутренний размер меньше или его размерное масштабирование (для плотности/размера выборки) меньше», но связано с BitmapFactory, а не с BitmapRegionDecoder , немного странно – Ninja

1

Это источник BitmapRegionDecoder#decodeRegion:

public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) { 
    checkRecycled("decodeRegion called on recycled region decoder"); 
    if (rect.left < 0 || rect.top < 0 || rect.right > getWidth() 
      || rect.bottom > getHeight()) 
     throw new IllegalArgumentException("rectangle is not inside the image"); 
    return nativeDecodeRegion(mNativeBitmapRegionDecoder, rect.left, rect.top, 
      rect.right - rect.left, rect.bottom - rect.top, options); 
} 

Как вы можете видеть, он просто называет нативный метод. Я не понимаю достаточно C++, чтобы увидеть, уменьшает ли метод растровое изображение (в соответствии с вашим флагом inDensity).

Другие два метода используют один и тот же нативный метод (nativeDecodeAsset), чтобы получить растровое изображение.

Номер 2 caches извлекаемый и, следовательно, требуется больше памяти.
После множества операций (проверка того, что битмап уже предварительно загружен или обналичен и другие вещи), он вызывает собственный метод для получения растрового изображения. Затем он кэширует выделение и устанавливает фоновое изображение.

Номер 3 довольно прямолинейный, он вызывает собственный метод после нескольких операций.


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

  1. Ваш первый attemp оценивает битмап вниз (флаг inDensity) и, следовательно, ему требуется меньше памяти.
  2. Все три метода нуждаются в более или менее одинаковом объеме памяти, номер 2 и 3 всего лишь немного больше. В вашем изображении используется ~ 16 МБ ОЗУ, что является максимальным размером кучи на некоторых телефонах. Номер 1 может находиться под этим пределом, а два других - немного выше порога.

Я предлагаю вам отладить эту проблему. В вашем манифесте установите android:largeHeap="true", чтобы получить больше памяти. Затем запустите 3 разных attemps и зарегистрируйте размер кучи и байты, выделенные растровым изображением.

long maxMemory = Runtime.getRuntime().maxMemory(); 
long usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); 
long freeMemory = maxMemory - usedMemory; 
long bitmapSize = bmp.getAllocationByteCount(); 

Это даст вам лучший обзор.

+0

«Ваш первый attemp масштабирует битмап вниз» другой метод также масштабируется, но неявно. – mmlooloo

+0

«Все три метода требуют более или менее одинакового объема памяти», это не так. – mmlooloo

+0

"android: largeHeap =" true "" Я не хочу его решать. Я хочу знать, почему я не уйду из памяти. – mmlooloo

0
  1. Вы не получаете исключение ООМ из-за этого

    options.inPreferredConfig = Bitmap.Config.ARGB_8888; 
    

Он уже дал here

public Bitmap.Config inPreferredConfig 

Добавлено в уровне API 1

Если это не нуль, декодер попытается использовать t o декодировать эту внутреннюю конфигурацию. Если это значение равно null или запрос не может быть удовлетворен, декодер попытается выбрать наилучшую конфигурацию соответствия, основанную на глубине экрана системы, и характеристики исходного изображения, например, если он имеет пиксель альфа (требуется конфигурация, которая также делает). По умолчанию изображение загружается конфигурацией ARGB_8888.

+0

Извините, но я не понимаю, что вы имеете в виду. 'Bitmap.Config.ARGB_8888' заставляет хранить растровое изображение как можно больше (4 байта на пиксель), поэтому я должен получить OOM определенно, но я не получаю :-( – mmlooloo

+0

И как явно указать какой-то параметр на значение по умолчанию, которое должно влиять на распределение памяти Или что-нибудь вообще? – nekavally

0

ОК, вплоть до ядра, различие между 1 и 2,3 заключается в том, что 1 не поддерживает девять патчей и очистителей. Поэтому, скорее всего, немного дополнительной памяти, выделенной для NinePatchPeeker для работы во время декодирования, является то, что запускает OOM в 2 и 3 (поскольку они используют тот же бэкэнд). В случае 1 он не выделяется.

Другое из этого числа я не вижу других вариантов. Если вы посмотрите на декодирование данных изображения, то черепичное декодирование использует немного больше памяти из-за индекса изображения, поэтому, если бы это было так, ситуация была бы противоположной: 1 будет бросать OOM, а 2,3 - нет.