Из памяти ошибка при загрузке изображений в Glide

редактировать:

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

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

android:largeHeap="true"

Скользить Версия:

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

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

Nexus устройство 6.0 версия

каждые изображения Я получаю от Json 800kb до 1mb.

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.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.run(FifoPriorityThreadPoolExecutor.java:118)

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

10 ответов


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

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

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

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

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

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

удачи.


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

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

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

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


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

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


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

Шаг 1: понять кэширование механизм в Glide

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

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

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


  1. убедитесь, что ImageView имеет match_parent или фиксированный dp как размеры wrap_content в качестве параметров делает скольжение полном разрешении загружать растровые изображения.
  2. .placeholder () показывает изображение вместо пустого пространства при загрузке большого растрового изображения
  3. .thumbnail (float) быстро загружает уменьшенную версию, в то время как большее изображение загружается в фоновом режиме
  4. и посмотрите вокруг вопросы Глайд, может быть, вы найдете что-то полезное.

я столкнулся с аналогичной проблемой . Я делюсь тем, как я ее решил . Создайте папку с именем drawable-nodpi положить golive_load_image и golive_cancel_im‌​age файл в эту папку и удалите эти два файла изображения из другого места, например drawable-ldpi,drawable-hdpi etc (если у вас есть). И добавить 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);

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

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;
        }
    }

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

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

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

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

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

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

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

  • взять дамп "кучи" (для анализа утечек памяти)

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

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

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

надеюсь, это поможет вы.


вероятно, для решения этой проблемы можно использовать другой подход. Для достижения этого вы можете использовать разные 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