OOM при использовании NetworkImageView (библиотеки Volley)

Фон

Использование библиотеки Volley https://android.googlesource.com/platform/frameworks/volley/+/d62a616ebca5bfa4f9ec5517208e13f2d501b69a/src/com/android/volley/toolbox/NetworkImageView.java "rel =" nofollow noreImagerer "> / a> - удобный способ отображения изображений из Интернета.

Однако в нем есть некоторые ошибки (как я писал здесь ).

эта проблема

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

Это означает, что если вы используете gridView с несколькими NetworkImageView в нем, и каждый из них показывает изображение с неизвестным разрешением (может быть маленьким, может быть большим), вы получите OOM.

например, вы можете установить URL-адрес этого объекта как этот и убедитесь сами, сколько памяти использует приложение после отображения растрового изображения по сравнению с тем, сколько оно использовало раньше.

Вопрос

Как я могу изменить способ декодирования растрового изображения в NetworkImageView?

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


person android developer    schedule 02.09.2013    source источник


Ответы (2)


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

  1. Прекратите использовать NetworkImageView. Используйте обычный ImageView и реализуйте прослушиватель для применения изображения, когда оно доступно. Это предварительное условие для шага 2. Использование NetworkImageView с методом get() может привести к проблемам в моем опыте`.
  2. Создайте ImageLoader и используйте метод get(), который получает ImageRequest. По возможности используйте необязательный конструктор, который принимает в качестве параметров maxHeight и maxWidth.
  3. Когда вы используете ранее упомянутый метод get() в ImageLoader, сохраните ссылку ImageContainer, которую возвращает этот метод, чтобы вы могли отменить запрос, если представление было переработано до завершения запроса.
  4. Обеспечьте хорошую реализацию ImageCache в конструкторе ImageLoader. Это снизит избыточность декодирования уже доступных растровых изображений.
  5. Если ваша архитектура позволяет это, попробуйте использовать метод recycle() для растровых изображений, но будьте осторожны, чтобы не утилизировать те, которые вам могут еще понадобиться.

РЕДАКТИРОВАТЬ: добавлены образцы кода

Фрагмент кода для (2) + (4)

// assuming sRequestQueue is your static global request queue 
// and `BitmapCache` is a good implementation for the `ImageCache` interface
sImageLoader = new ImageLoader(sRequestQueue, new BitmapCache());

Фрагмент кода для (3) в предположении, что шаблон ViewHolder и imageContainer является членом класса ViewHolder. Принцип применим к любой архитектуре.

// when applying a new view cancel the previous request first

if (imageContainer != null) {
    imageContainer.cancelRequest();
}

// calculate the max height and max width

imageContainer = sImageLoader.get(imageUrl, 
    new DefaultImageListener(image), maxWidth, maxHeight);

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

private class DefaultImageListener implements ImageListener {
    private ImageView imageView;

    public DefaultImageListener(ImageView view) {
        imageView = view
    }

    @Override
    public void onErrorResponse(VolleyError error) {
        //handle errors
    }

    @Override
    public void onResponse(ImageContainer response, boolean isImmediate) {
        if (response.getBitmap() != null) {
            imageView.setImageBitmap(response.getBitmap());
        }
    }
}
person Itai Hanski    schedule 03.09.2013
comment
Не могли бы вы показать образец кода? Кроме того, зачем Google создал этот класс, если он не может обрабатывать большие изображения каким-либо хорошим / индивидуальным способом? - person android developer; 03.09.2013
comment
NetworkImageView - это синтаксический сахар для базовой загрузки удаленных изображений. Если вы послушаете основной доклад, то увидите, что Фикус (ведущий разработчик) говорит, что NetworkImageView в основном, я цитирую, для ленивых разработчиков (он имеет в виду это в хорошем смысле). Это хорошее базовое решение, но если вам нужно что-то конкретное, вам придется использовать другой механизм. Я отредактирую свой ответ и поставлю несколько образцов. - person Itai Hanski; 03.09.2013
comment
Ленивый разработчик - обычно хороший разработчик, который берет на себя работу других вместо того, чтобы заново изобретать колесо и тратить время на новые ошибки. это не так, поскольку этого класса недостаточно для изображений, которые могли бы заполнить память. у него даже нет способа исправить эту проблему индивидуальным способом. - person android developer; 03.09.2013
comment
Это не совсем правильно, я только что опубликовал несколько пользовательских исправлений. Также вещи никогда не бывают черно-белыми. Вы должны принять во внимание, что Volley - новый продукт, и, как и все новые продукты, он постоянно развивается и расширяется. - person Itai Hanski; 03.09.2013
comment
Вы тестировали свой код на очень больших изображениях, вроде того, о котором я писал? - person android developer; 09.09.2013
comment
Я не знаю, что переводится как супер большой, но я пробовал это с фотографиями обычного размера, которые вы найдете на большинстве новостных сайтов. Вы можете попробовать это, масштабирование изображения должно быть в порядке. В любом случае, я придерживаюсь этих передовых методов. - person Itai Hanski; 10.09.2013
comment
super large достаточно велик, чтобы ваше устройство перешло в режим OOM, если вы действительно декодируете растровое изображение обычным способом без понижающей дискретизации. я думаю, что если код обрабатывает даже такие изображения, он может обрабатывать и изображения меньшего размера ... - person android developer; 10.09.2013
comment
Посмотрите на фрагмент (3). Есть пример как для метода get(), так и для слушателя, о котором я упоминал на шаге (1). - person Itai Hanski; 11.09.2013
comment
Давайте рассмотрим то, что я написал: Используйте обычный ImageView и реализуйте прослушиватель для применения изображения, когда оно доступно. В коде шага (3) я написал: imageContainer = sImageLoader.get(imageUrl, new DefaultImageListener(image), maxWidth, maxHeight); Предполагая, что image это ваш ImageView, который имеет ссылка хранится в классе ViewHolder. Это охватывает шаг (1). - person Itai Hanski; 11.09.2013
comment
Опять же, я все еще не понимаю, что это значит, поэтому я надеялся, что вы покажете мне код. - person android developer; 11.09.2013
comment
позвольте нам продолжить это обсуждение в чате - person Itai Hanski; 11.09.2013

Я нашел лучшее решение в своем поиске: -)

NetworkImageView знает свою ширину в строке № - 104 и высоту в строке № - 105 в ссылке NetworkImageView.java

Ниже приведен точный код на NetworkImageView.java

    private void loadImageIfNecessary(final boolean isInLayoutPass) {
    int width = getWidth(); // at line no 104
    int height = getHeight(); // at line no 105

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

В строке 141 NetworkImageView. java вызывает метод ImageLoader # get (String requestUrl, final ImageListener listener) без ширины и высоты. Измените этот вызов на ImageLoader # get (String requestUrl, ImageListener imageListener, int maxWidth, int maxHeight).

Замените код из строк 141 на 172 в NetworkImageView.java с приведенным ниже кодом

ImageContainer newContainer = mImageLoader.get(mUrl,
            new ImageListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    if (mErrorImageId != 0) {
                        setImageResource(mErrorImageId);
                    }
                }

                @Override
                public void onResponse(final ImageContainer response, boolean isImmediate) {
                    // If this was an immediate response that was delivered inside of a layout
                    // pass do not set the image immediately as it will trigger a requestLayout
                    // inside of a layout. Instead, defer setting the image by posting back to
                    // the main thread.
                    if (isImmediate && isInLayoutPass) {
                        post(new Runnable() {
                            @Override
                            public void run() {
                                onResponse(response, false);
                            }
                        });
                        return;
                    }

                    if (response.getBitmap() != null) {
                        setImageBitmap(response.getBitmap());
                    } else if (mDefaultImageId != 0) {
                        setImageResource(mDefaultImageId);
                    }
                }
            }, width, height);
person Gangadhar Nimballi    schedule 16.01.2014
comment
не могли бы вы показать точный код и куда его поместить (а не только номер строки)? - person android developer; 16.01.2014
comment
так это решает проблему? может стоит написать об этом по вопросам / запросам сайта github? к сожалению, я не могу проверить то, что вы написали, так как перестал пользоваться этой библиотекой. Может быть, когда-нибудь я попробую снова. - person android developer; 18.01.2014
comment
Я тоже этим закончил! Хотя я не уверен в кэшированном изображении ... Если вы начнете с Gridview и с этим изменением кода, означает ли это, что кешированное изображение будет меньше? И когда вы переключаетесь в полноэкранный режим, щелкнув изображение GridView, будет ли оно повторно использовать изображение с низким размером образца? - person alpinescrambler; 04.02.2014
comment
Я не уверен, что размер кеша невелик, но когда вы устанавливаете изображение в NetworkImageView, он устанавливает небольшой размер изображения. - person Gangadhar Nimballi; 04.02.2014
comment
предотвращает ли это все ваши ошибки Out of Memory? - person CQM; 11.06.2014