Подтвердить что ты не робот

Почему я НЕ получаю исключение из памяти?

У меня есть изображение с высоким разрешением (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, я просто хочу знать разницу)

4b9b3361

Ответ 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);
}

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

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

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

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


Вывод: Для меня трудно сказать, какой сценарий здесь применим, но он должен быть одним из двух.
  • Ваш первый attemp масштабирует битмап вниз (флаг inDensity) и, следовательно, ему требуется меньше памяти.
  • Все три метода нуждаются в более или менее одинаковом объеме памяти, номер 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();

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

Ответ 2

Слишком много деталей изображения приводит к нехватке памяти.

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

1

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

конструктор (InputStream is, boolean isShareable) использует поток, который не исчерпывает память.

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

Обратитесь: BitmapRegionDecoder выведет свой запрошенный контент в предоставленный Bitmap, обрезая, если размер выходного контента (пост-масштабирование) больше, чем предоставленный Bitmap. Предоставляемая ширина, высота и растровая карта Bitmap.Config не будут изменены

2,3

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

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

Извините за английский.

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

Ответ 3

  • Вы не получаете исключение OOM из-за этого

    options.inPreferredConfig = Bitmap.Config.ARGB_8888;
    

Это уже указано здесь

    public Bitmap.Config inPreferredConfig

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

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

Ответ 4

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

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