Ошибка нехватки памяти при загрузке большего количества изображений в Glide

Отредактировано:

В моем приложении я загружаю более 300 изображений на домашней странице. я использовалglide загрузить изображения. я собираюсьOut of Memory Error.

Я использовал большую кучу правда вманифест :

android:largeHeap="true"

Версия Glide:

compile 'com.github.bumptech.glide:glide:3.7.0'

Версия устройства / Android:

Версия Nexus Device 6.0

Каждое изображение, которое я получаю от Json, будет от 800 КБ до 1 МБ.

activity_layout:

<RelativeLayout
    android:id="@+id/home_layout_bottom"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_below="@+id/home_layout_top_recycler"
    android:layout_margin="5dp">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_list_tab_home_recycler"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        android:scrollbars="vertical"
        android:visibility="visible" />

    <TextView
        android:id="@+id/no_user_posts_item_tv_recycler"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/rv_list_tab_home_recycler"
        android:layout_marginTop="80dp"
        android:layout_centerHorizontal="true"
        android:text="@string/txt_no_posts_available"
        android:textColor="@color/txt_common_black"
        android:textSize="@dimen/txt_size" />
</RelativeLayout>

код адаптера:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;

    final HomePostItems rowItem = getItem(position);

    LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
    if (convertView == null) {

        convertView = mInflater.inflate(R.layout.lv_adapter_post_items_layout, null);

      holder = new ViewHolder();

      holder.ivPostedImage = (ImageView) convertView.findViewById(R.id.iv_posted_img);


        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }

      ..................

          Glide.with(context).load(rowItem.getPosteduserpostimage())
                        .placeholder(R.drawable.golive_load_image).error(R.drawable.golive_cancel_image)
                        .override(600, 200)
                        .into(holder.ivPostedImage);

adapter_layout.xml:

<RelativeLayout
    android:id="@+id/rl_lv_user_post_adapter_img_holder_home"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:layout_marginLeft="1dp"
    android:layout_marginRight="1dp"
    android:layout_below="@+id/tv_user_posted_msg_post_items_home" >

    <ImageView
        android:id="@+id/iv_posted_img_home"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true"
        android:scaleType="fitXY"
        android:background="#ffffff"
        android:contentDescription="@string/cont_desc"/>
</RelativeLayout>

Logcat:

Request threw uncaught throwable
java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: Failed to allocate a 6365196 byte allocation with 865912 free bytes and 845KB until OOM
at java.util.concurrent.FutureTask.report(FutureTask.java:94)
at java.util.concurrent.FutureTask.get(FutureTask.java:164)
at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor.afterExecute(FifoPriorityThreadPoolExecutor.java:96)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1121)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.java:118)
Caused by: java.lang.OutOfMemoryError: Failed to allocate a 6365196 byte allocation with 865912 free bytes and 845KB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:635)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:611)
at com.bumptech.glide.load.resource.bitmap.Downsampler.decodeStream(Downsampler.java:329)
at com.bumptech.glide.load.resource.bitmap.Downsampler.downsampleWithSize(Downsampler.java:220)
at com.bumptech.glide.load.resource.bitmap.Downsampler.decode(Downsampler.java:153)
at com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder.decode(StreamBitmapDecoder.java:50)
at com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder.decode(StreamBitmapDecoder.java:19)
at com.bumptech.glide.load.resource.bitmap.ImageVideoBitmapDecoder.decode(ImageVideoBitmapDecoder.java:39)
at com.bumptech.glide.load.resource.bitmap.ImageVideoBitmapDecoder.decode(ImageVideoBitmapDecoder.java:20)
at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decodeBitmapWrapper(GifBitmapWrapperResourceDecoder.java:121)
at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decodeStream(GifBitmapWrapperResourceDecoder.java:94)
at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.java:71)
at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.java:61)
at com.bumptech.glide.load.resource.gifbitmap.GifBitmapWrapperResourceDecoder.decode(GifBitmapWrapperResourceDecoder.java:22)
at com.bumptech.glide.load.engine.DecodeJob.decodeFromSourceData(DecodeJob.java:190)
at com.bumptech.glide.load.engine.DecodeJob.decodeSource(DecodeJob.java:177)
at com.bumptech.glide.load.engine.DecodeJob.decodeFromSource(DecodeJob.java:128)
at com.bumptech.glide.load.engine.EngineRunnable.decodeFromSource(EngineRunnable.java:122)
at com.bumptech.glide.load.engine.EngineRunnable.decode(EngineRunnable.java:101)
at com.bumptech.glide.load.engine.EngineRunnable.run(EngineRunnable.java:58)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
at java.lang.Thread.run(Thread.java:818) 
at com.bumptech.glide.load.engine.executor.FifoPriorityThreadPoolExecutor$DefaultThreadFactory$1.run(FifoPriorityThreadPoolExecutor.java:118)

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

 Kaushik27 сент. 2016 г., 13:14
@Naruto код getView и SS ListView
 Sakchham01 окт. 2016 г., 11:06
Пожалуйста, обновите код для вашего адаптера
 Stephen27 сент. 2016 г., 13:11
@ Каушик вы можете проверитьВот
 Kaushik27 сент. 2016 г., 13:38
@Naruto код getView в порядке
 Kaushik27 сент. 2016 г., 13:03
@ Наруто, пожалуйста, поделитесь кодом адаптера с желанием сс
 Kaushik27 сент. 2016 г., 13:43
 Jas25 июл. 2016 г., 08:19
Где вы сохраняете старый рингтон?
 Stephen27 сент. 2016 г., 13:42
@Kaushik ок. У тебя есть другие предложения?
 Stephen27 сент. 2016 г., 13:23
@ Каушик, пожалуйста, проверьтеэтот
 Stephen27 сент. 2016 г., 12:57
@ Каушик, как вы сказали, я добавилGlide.with(context).load(rowItem.getPosteduserpostimage()).override(600, 200).placeholder(R.drawable.golive_load_image).error(R.drawable.golive_cancel_image).into(holder.ivPostedImage);      .into (imageViewResize); это должно добавить centercrop или fitcenter? все еще я получаю ООМ
 Stephen26 сент. 2016 г., 17:34
@invisbo после прокрутки изображений я получаю эту проблему.
 invisbo26 сент. 2016 г., 17:30
Вы получаете OOM при прокрутке или при первой загрузке страницы?

Ответы на вопрос(10)

как я решил это. Создать папку с именемdrawable-nodpi положить вашиgolive_load_image а такжеgolive_cancel_im‌​age файл в эту папку, и удалите эти два файла изображения из другого места, какdrawable-ldpi,drawable-hdpi и т. д. (если у вас есть). И добавитьskipMemoryCache( true )

     Glide.with(context).load(rowItem.getPosteduserpostimage())
                            .skipMemoryCache( true )
                            .placeholder(R.drawable.golive_load_image).error(R.drawable.golive_cancel_image)
                            .override(600, 200)
                            .into(holder.ivPostedImage);
 Carlos Daniel25 мар. 2019 г., 17:22
Согласно официальной документации Glidebumptech.github.io/glide/doc/caching.html не рекомендуется пропускатьMemoryCache ->В общем, вы хотите избежать пропуска кэшей. Загрузка изображения из кэша намного быстрее, чем его извлечение, декодирование и преобразование для создания нового эскиза.
Убедитесь, что у ImageView есть match_parent или фиксированный dp, поскольку размеры wrap_content заставляют Glide загружать растровые изображения с полным разрешением..placeholder () показывает изображение вместо пустого места при загрузке большого растрового изображения.thumbnail (float) быстро загружает версию с пониженной дискретизацией, в то время как большее изображение загружается в фоновом режимеТакже посмотрите вокругВопросы скольженияМожет быть, вы найдете что-то полезное.
 Sreehari03 окт. 2016 г., 12:30
Для дополнительной информацииstackoverflow.com/questions/30718303/...

Out of Memory ошибки, вам нужно использовать несколько небольших шагов, чтобы уменьшить вероятность не получитьOOM's.

Шаг 1: Понятьмеханизм кеширования в скольжении

Шаг 2: Я предпочитаю загружатьthumbnails в переработчик

Glide  
    .with( context )
    .load( UsageExampleGifAndVideos.gifUrl )
    .thumbnail( 0.1f )
    .into( imageView2 );

Не забывайте всегда запрашивать изображение небольшого размера, если изображения большего размера или HD не требуются.

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

if (view.getContext() != null) {
        switch (scrollState) {
            case SCROLL_STATE_IDLE:
                Glide.with(view.getContext()).resumeRequests();
                break;
            case SCROLL_STATE_TOUCH_SCROLL:
            case SCROLL_STATE_FLING:
                Glide.with(view.getContext()).pauseRequests();
                break;
        }
    }
 Aba10 апр. 2019 г., 20:27
Я попробовал это, и это не очень хорошо работало.

recyclerView вместоListView, Это многоразовый предмет для рендеринга предметов. я используюglide сrecyclerView где я загружаю обои с более чем 100 элементами.

В ListView каждый раз, когда вы создаете представление, если у вас есть более 100 представлений, и он будет создавать более 100 представлений, где, как и в программе повторного просмотра, он создает количество видимых элементов на экране +2.

 Roman_D03 окт. 2016 г., 07:46
Хорошо, тогда проверьте размер изображения (необработанный размер растрового изображения, а не размер JPEG), насколько он велик? Происходит ли ООМ, даже если вы установилиskipMemoryCache( true ) а также.diskCacheStrategy(DiskCacheStrategy.NONE) ?
 Roman_D03 окт. 2016 г., 07:27
ListView не создает более 100 представлений, но использует старый вид -convertView, Так что это не тот случай. @ Наруто какая версия Android на вашем тестовом устройстве?
 Stephen28 сент. 2016 г., 07:31
пожалуйста, проверьте мой отредактированный пост.
 Sakchham01 окт. 2016 г., 11:06
ListView тоже не будет вызывать какие-либо OOMEs
 subrahmanyam boyapati28 сент. 2016 г., 09:03
Похоже, вы не обновили код адаптера
 Stephen03 окт. 2016 г., 07:35
@Roman_Donchenko Зефирная версия 6.0
Решение Вопроса

удаление вложенной прокрутки размещено выше. Почему произошла ошибка OutOfMemory, означает, что при загрузке более 200 изображений на домашней странице она загружает все 200 изображений из-за использования вложенной прокрутки над обзором реселлера.

Поэтому я не могу проверить ширину и высоту изображения Logcat по одному в адаптере.

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

Также проверьтеэтот, как использовать прокрутку вместо вложенного просмотра прокрутки.

но вы должны помнить об этом при загрузке изображений в списке с помощью Glide.

Основной угрожающей частью вашей проблемы является размер изображения. Изображение, которое вы получаете, составляет почти 1 МБ каждый! Что на самом деле слишком велико для отображения их в списке, содержащем более 300 элементов. Поэтому, если вы работаете и на стороне сервера, всегда рекомендуется иметь изображения нескольких разных размеров.

Например, в случае показа списка друзей вместе с их фотографиями в профиле, я бы посоветовал вам сначала получить весь список с сервера. Затем загрузите все изображения профиля и сохраните их локально. Затем заполнитеListView, И наиболее важной частью является то, что при загрузке изображения профиля пользователя на сервер, после его загрузки, сервер должен сохранять его несколько размеров, например, версия с низким, средним и высоким разрешением. Так, чтобы при показе картинки профиля URL дляListView сервер может предоставлять изображения с низким разрешением, так как они, скорее всего, будут использоваться для миниатюр.

С помощьюRecyclerView вместоListView это тоже хороший звонок. Но это не решит проблему, которая возникла у вас, когда вы работаете с недорогим устройством.

OMM не имеет ничего общего с вами, может решить программно. Вам нужно изменить размер изображения до более низкого разрешения.

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

Удачи.

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

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

Насколько я помню, OOM всегда была ошибкой использования,largeHeap просто задержит это; или если это большая нагрузка, то, возможно, это невозможно. Поэтому я предлагаю вам следоватьэтот ссылка для диагностики утечек памяти.

Стек следовOutOfMemoryErrors не помогает вообще для их диагностики. Он просто говорит вам, что он сломан и что-то наполнило память. Это заполнение происходит задолго до того, как было выдано фактическое исключение Это также подразумевает, что обычно все бросаетOOM на самом деле не виновник. Единственное исключение из этого - когда объем выделенной памяти для подражателя просто слишком велик, например: выделяемый массив больше, чем максимальный объем памяти, тогда вы знаете, что некоторые вычисления действительно пошли не так, как если бы изображение размером 32000x32000 @ 4 займет около 4 ГБ памяти.

Если вы можете воспроизвести: получите дамп кучи и проанализируйте использование вашего приложения. Обычные шаги диагностики OOM:

Воспроизвести исключение (подождите, пока вы не увидите его в LogCat)

Возьмите дамп кучи (для анализа утечек памяти)

Проанализируйте это для больших объектов и утечек

В приведенной выше общей ссылке есть еще несколько ссылок, касающихсяhow to take heap dump? а такжепроблемы которые идентичны с этим.

Поэтому я предлагаю вам проанализировать утечки памяти и предпринять необходимые шаги для предотвращения OOM.

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

Ваши изображения не должны быть слишком большими (если они есть, используйте.thumbnail(...f) )использование.skipMemoryCache(true) если вас не заставляют хранить изображения в кешеты можешь использовать.diskCacheStrategy(DiskCacheStrategy.NONE) отключить кеш диска

для решения этой проблемы можно использовать другой подход. Для достижения этого вы можете использовать другойImageAdapter с

Glide.with(mActivity).loadFromMediaStore(_imageInfo.getmUri())

это не сбой использованияMediaStoreThumbFetcher

Для большего контроля над нагрузкой выполните следующие действия, используя Glide v4.

// usage:
Glide.with(mActivity).load(_imageInfo)....

// in GlideModule.registerComponents
registry.prepend(ImageInfo.class, ImageInfo.class, new UnitModelLoader.Factory<ImageInfo>());
registry.prepend(ImageInfo.class, Bitmap.class, new ImageInfoBitmapDecoder(context));

class ImageInfoBitmapDecoder implements ResourceDecoder<ImageInfo, Bitmap> {
    private final ContentResolver contentResolver;
    private final BitmapPool pool;
    public ImageInfoBitmapDecoder(Context context) {
        this.contentResolver = context.getContentResolver();
        this.pool = Glide.get(context).getBitmapPool();
    }
    @Override public boolean handles(ImageInfo source, Options options) { return true; }
    @Override public @Nullable Resource<Bitmap> decode(ImageInfo source, int width, int height, Options options) {
        Bitmap thumb = Thumbnails.getThumbnail(contentResolver, source.getmId(), Thumbnails.MINI_KIND, null);
        return BitmapResource.obtain(thumb, pool);
    }
}

Используя следующие API, мы можем определить оставшуюся свободную память и размер растрового изображения.

Вы можете проверить доступную память и растровые данные (при необходимости) в качестве предварительной проверки

Проверьте количество свободной памяти осталось

public static final float BYTES_IN_MB = 1024.0f * 1024.0f;

    public static float megabytesFree() {
        final Runtime rt = Runtime.getRuntime();
        final float bytesUsed = rt.totalMemory();
        final float mbUsed = bytesUsed/BYTES_IN_MB;
        final float mbFree = megabytesAvailable() - mbUsed;
        return mbFree;
    }

    public static float megabytesAvailable() {
        final Runtime rt = Runtime.getRuntime();
        final float bytesAvailable = rt.maxMemory();
        return bytesAvailable/BYTES_IN_MB;
}

Проверьте, насколько большой растровое изображение мы хотим загрузить

private void readBitmapInfo() {
        final Resources res = getActivity().getResources();
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, R.drawable.brasil, options);
        final float imageHeight = options.outHeight;
        final float imageWidth = options.outWidth;
        final String imageMimeType = options.outMimeType;
        Log.d(TAG, "w,h, type:"+imageWidth+", "+imageHeight+", "+imageMimeType);
        Log.d(TAG, "estimated memory required in MB: "+imageWidth * imageHeight * BYTES_PER_PX/MemUtils.BYTES_IN_MB);
}

Для более подробной информации пройдитеJava-методы для проверки памяти и растровых изображений а такжеобсуждение GitHub

Ваш ответ на вопрос