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

Распределение растровых изображений, используя BitmapFactory.Options.inBitmap, вызывает IllegalArgumentException

Я получаю следующее исключение: Дешифрование задачи в существующее растровое изображение при установке inBitmap в true;

Вызвано: java.lang.IllegalArgumentException: проблема декодирования в существующую растровое изображение
на android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:460)
...

Интересно, что тот же код не работает в разных местах при запуске:

  • API: 4.4.2, Nexus 4
  • API: 4.3.1, Samsung s3

Это мой код, который является копией, как показано в этом видео DevBytes: Bitmap Allocation.

private BitmapFactory.Options options;
private Bitmap reusedBitmap;

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

    final ImageView imageView = (ImageView) findViewById(R.id.image_view);

    // set the size to option, the images we will load by using this option
    options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    options.inMutable = true;
    BitmapFactory.decodeResource(getResources(), R.drawable.img1, options);

    // we will create empty bitmap by using the option
    reusedBitmap = Bitmap.createBitmap(options.outWidth, options.outHeight, Bitmap.Config.ARGB_8888);

    // set the option to allocate memory for the bitmap
    options.inJustDecodeBounds = false;
    options.inSampleSize = 1;
    options.inBitmap = reusedBitmap;

    // #1
    reusedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img1, options);
    imageView.setImageBitmap(reusedBitmap);

    imageView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            options.inBitmap = reusedBitmap;
            // #2
            reusedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.img2, options);
            imageView.setImageBitmap(reusedBitmap);

        }
    });
}

  • Nexus 4, сбой на BitmapFactory.decodeResource() на // #1
  • S3, передает # 1 и отображает первое изображение, но позже падает, нажимая на изображение BitmapFactory.decodeResource() на // #2

Несколько заметок:

  • Изображения имеют одинаковый размер. Я пробовал jpg и png. Не работает на обоих.
  • Растровые изображения изменяемы.
  • Я проверил с помощью этого метода canUseForInBitmap, описанного здесь .

Вопрос:

Как правильно использовать это свойство inBitmap?

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

- Изменить (вопрос все еще открыт) -

Извините, что не объяснил причину, почему я пытаюсь повторно использовать растровые изображения таким образом.
Причина этого - GC, которая блокируется каждый раз, когда он решает освободить память.
Функция inBitmap должна помочь нам повторно использовать растровое изображение без выделения новой памяти, которая заставит GC очистить уже выделенную память.

Например, если я использую этот общий подход:

Log.i("my_tag", "image 1");
imageView.setImageResource(R.drawable.img1);
Log.i("my_tag", "image 2");
imageView.setImageResource(R.drawable.img2);
Log.i("my_tag", "image 3");
imageView.setImageResource(R.drawable.img3);

Тогда это будет работа GC:

I/my_tag  ( 5886): image 1
D/dalvikvm( 5886): GC_FOR_ALLOC freed 91K, 2% free 9113K/9240K, paused 15ms, total 15ms
I/dalvikvm-heap( 5886): Grow heap (frag case) to 19.914MB for 11520016-byte allocation
D/dalvikvm( 5886): GC_FOR_ALLOC freed <1K, 1% free 20362K/20492K, paused 13ms, total 13ms
I/my_tag  ( 5886): image 2
D/dalvikvm( 5886): GC_FOR_ALLOC freed 11252K, 2% free 9111K/9236K, paused 15ms, total 15ms
I/dalvikvm-heap( 5886): Grow heap (frag case) to 19.912MB for 11520016-byte allocation
D/dalvikvm( 5886): GC_FOR_ALLOC freed <1K, 1% free 20361K/20488K, paused 35ms, total 35ms
I/my_tag  ( 5886): image 3
D/dalvikvm( 5886): GC_FOR_ALLOC freed 11250K, 2% free 9111K/9236K, paused 15ms, total 15ms
I/dalvikvm-heap( 5886): Grow heap (frag case) to 19.913MB for 11520016-byte allocation
D/dalvikvm( 5886): GC_FOR_ALLOC freed <1K, 1% free 20361K/20488K, paused 32ms, total 32ms

Это более чем 100 мсек заблокированного основного потока!

То же самое произойдет, если я буду decodeResource() без опции inBitmap. Итак, вопрос по-прежнему открыт, как использовать это свойство?

4b9b3361

Ответ 1

Я попробовал свой код с эмулированным Nexus 4.
У меня был файл ic_launcher.png по умолчанию, который я копировал и вставлял его два раза в drawable-mdpi (как я обычно делаю). Я переименовал два новых файла в соответствии с именами в вашем коде (чтобы у меня было меньше изменений, чтобы сделать там).
Когда я запускаю приложение, я наблюдал то же, что и вы. После нескольких разных попыток я решил скопировать новые png в другие доступные папки - так они присутствовали в:

  • рисуем-ИПЧР
  • рисуем-MDPI
  • drawable-xhdpi
  • рисуем-xxhdpi

Я запускаю приложение и работает!

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

Ответ 2

Немного странно, что вы создаете новое растровое изображение, а затем пытаетесь его повторно использовать. Почему бы просто не позволить decodeResource создать новое растровое изображение в первую очередь? Я подозреваю, что ваша проблема в № 2, хотя это то, что после того, как вы установили ImageView для использования растрового изображения, он больше не сможет его повторно использовать (потому что он уже используется). IllegalArgumentException упоминается в docs:

Если операция декодирования не может использовать это растровое изображение, метод декодирования будет возвращать значение null и будет вызывать исключение IllegalArgumentException.

Что касается обходного пути, вы можете попытаться сохранить два растровых изображения вокруг и переключиться вокруг того, на который указывает ImageView, например:

  • декодировать в растровое изображение 1, точка ImageView в растровое изображение 1
  • декодировать в растровое изображение 2, точка ImageView в растровое изображение 2
  • повторить с шага 1

Ответ 3

Вам нужно будет установить options.inMutable в true до # 1 и # 2 в вашем коде. Это может звучать немного, но это немного мин, оставшихся на том, что вы наступили.

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

Ответ 4

Почему вы декодируете растровые изображения? Просто выполните:

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

    final ImageView imageView = (ImageView) findViewById(R.id.image_view);
    imageView.setImageResource(R.drawable.img1);

    imageView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            imageView.setImageResource(R.drawable.img2);
        }
    });
}

Вы уверены, что оба изображения имеют одинаковые размеры? Согласно документации

Если установлено [inBitmap], декодировать методы, которые принимают объект Options, будут попытайтесь повторно использовать это растровое изображение при загрузке содержимого. Если декодирование операция не может использовать это растровое изображение, метод декодирования вернет значение null и будет вызывать исключение IllegalArgumentException. Текущая реализация требует, чтобы повторно используемое растровое изображение было изменчивым, и в результате повторное использование растрового изображения будет продолжать изменяться даже при декодировании ресурс, который обычно приводит к неизменяемому растровому изображению.

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

Использование с BitmapFactory

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

До применения KITKAT применяются дополнительные ограничения: декодированное изображение (как ресурс, так и поток) должны быть в формате jpeg или png. Поддерживаются только растровые изображения равного размера, а inSampleSize - 1. Кроме того, конфигурация повторно используемого растрового изображения переопределяет настройка inPreferredConfig, если установлена.


EDIT:

Если у вас большой битмап ресурсов, просто загрузите его в asynctask... Подробнее ЗДЕСЬ, но это можно сделать гораздо проще.

Ответ 5

Я не уверен в проблеме, с которой вы столкнулись с Samsung S3 на # 2. Но проблема, с которой вы столкнулись с Nexus 4, может быть связана с тем, что вы помещаете два изображения в неправильную папку с возможностью dpi. Поэтому, когда он пытается декодировать растровое изображение, он не может найти ресурсы.

Мой телефон имеет плотность экрана hdpi, сначала я пытаюсь поместить два изображения в drawable-mdpi, и я столкнулся с проблемой С# 1. Поэтому я изменил его на drawable-hdpi, и он сработал.

Ответ 6

Просто поместите largeHeap = "true" в тег приложения (AndroidManifest.xml)