【问题标题】:Out Of Memory Error When loading more images in Glide在 Glide 中加载更多图像时出现内存不足错误
【发布时间】:2016-07-25 05:59:24
【问题描述】:

已编辑:

  • 在我的应用程序中,我在主页中加载了 300 多张图片。我使用glide 加载图像。我收到了Out of Memory Error

我在 ma​​nifest 中使用了大堆 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$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 问题。如果您已经熟悉此问题,请分享您的建议。

【问题讨论】:

  • 旧铃声保存在哪里?
  • 在滚动或首次加载页面时是否会出现 OOM?
  • @invisbo 在滚动图像后我遇到了这个问题。
  • @Kaushik 正如你所说,我已经添加了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?我仍然在OOM
  • @Naruto 你能把适配器的代码分享给欲望ss吗

标签: java android out-of-memory


【解决方案1】:
  • 我通过删除嵌套滚动视图解决了这个问题 回收站视图。为什么发生 OutOfMemory 错误意味着,当加载更多 主页中的图片超过 200 张,由于使用,它正在加载所有 200 张图片 recyclerview 上方的嵌套滚动视图。

  • 所以我无法一一检查logcat图像视图的宽度和高度 适配器。

  • 删除嵌套滚动视图后修复内存不足错误。因为它 回家时将仅加载设备中显示的 3 张图像 活动。

  • 同时检查 this,如何使用滚动而不是嵌套滚动视图。

【讨论】:

    【解决方案2】:

    这不是您问题的确切解决方案,但在使用 Glide 将图像加载到列表中时,您需要牢记这些事情。

    您的问题的主要威胁部分是图像大小。你得到的图像几乎每个 1mb!这实际上太大了,无法将它们显示到包含 300 多个项目的列表中。所以如果你也在做服务器端,它总是建议有几种不同尺寸的图像。

    例如,如果显示好友列表及其头像,我建议您首先从服务器获取整个列表。然后获取所有配置文件图像并将它们存储在本地。然后填充ListView。最重要的部分是在将用户的个人资料图片上传到服务器时,上传后,服务器需要保留几种尺寸,例如低、中、高分辨率版本。因此,在为ListView 提供个人资料图片网址时,服务器可能会提供低分辨率的图像,因为它们最有可能用于缩略图。

    使用RecyclerView 代替ListView 也是一个不错的选择。但是当您使用低端设备时,它不会解决您在这里遇到的问题。

    OMM 与您无关,您可以通过编程方式解决。您需要将图像大小调整为较低的分辨率版本。

    您也可以查看 Glide 的 caching mechanism。我建议您使用缓存策略,这样每次您不必从服务器加载图像。

    祝你好运。

    【讨论】:

      【解决方案3】:

      使用 Glide 并不能保证没有Out of Memory 错误,您需要使用几个小步骤来降低无法获得OOM's 的概率。

      第 1 步:了解 Glide 中的 caching mechanism

      第 2 步:我更喜欢将 thumbnails 加载到 recyclerview 中

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

      如果需要更大或高清图像,请记住始终请求小尺寸图像 不需要。

      【讨论】:

        【解决方案4】:
        1. 确保 ImageView 具有 match_parent 或固定 dp 作为尺寸 wrap_content 让 Glide 加载全分辨率位图。
        2. .placeholder() 在加载大位图时显示图像而不是空白空间
        3. .thumbnail(float) 快速加载下采样版本,而更大的图像在后台加载
        4. 也看看Glide issues,也许你会发现一些有用的东西。

        【讨论】:

        【解决方案5】:

        使用recyclerView 代替ListView。它可重复使用单个项目来渲染项目。我正在使用 gliderecyclerView,我正在加载包含 100 多个项目的壁纸。

        在 ListView 中,每次创建视图时,如果您有 100 多个视图,它将创建 100 多个视图,而在 recyclerview 中,它会在屏幕 +2 中创建多少可见项目。

        【讨论】:

        • 一个 ListView 也不会导致任何 OOMEs
        • ListView 不会创建 100 多个视图,而是重用旧视图 - convertView。所以这里没有这种情况。 @Naruto 您的测试设备上的 Android 版本是什么?
        • 好的,然后检查图像大小(原始位图大小,而不是JPEG大小),它有多大?即使设置skipMemoryCache( true ).diskCacheStrategy(DiskCacheStrategy.NONE)也会发生OOM吗?
        【解决方案6】:

        原因是在滚动时,即使相关视图从列表中删除,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;
                }
            }
        

        【讨论】:

          【解决方案7】:

          我遇到了类似的问题。我正在分享我解决它的方式。创建一个名为drawable-nodpi 的文件夹,将您的golive_load_imagegolive_cancel_im‌​age 文件放入该文件夹,然后从其他位置删除这两个图像文件,例如drawable-ldpidrawable-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);
          

          【讨论】:

          • 根据 Glide 的官方文档bumptech.github.io/glide/doc/caching.html 不建议跳过MemoryCache --> 一般你要尽量避免跳过缓存。从缓存加载图像比检索、解码和转换图像以创建新缩略图要快得多。
          【解决方案8】:
          • 图片不能太大(如果是,请使用.thumbnail(...f)
          • 如果您不强制将图像保存在缓存中,请使用 .skipMemoryCache(true)
          • 您可以使用.diskCacheStrategy(DiskCacheStrategy.NONE) 停用磁盘缓存

          【讨论】:

            【解决方案9】:

            为了防止内存不足错误,可以采取预防措施以确保不会发生。所以这个问题的答案实际上是一堆可以提出的建议。我也是。

            正如 @Reaz Murshed 所建议的那样,我还建议使用几种不同尺寸的图像。除此之外,我还想补充一些可以帮助您分析并解决此问题的内容。

            据我所知,OOM 一直是使用错误,largeHeap 只会延迟它;或者如果它是一个大负载,那么也许这是不可能的。所以我建议你按照this 链接来诊断内存泄漏。

            OutOfMemoryErrors 的堆栈跟踪对诊断毫无帮助 他们。它只是告诉你它坏了,有些东西填满了 记忆。这种填充发生在实际异常发生之前 抛出。这也意味着通常抛出OOM 的东西不是 实际上是罪魁祸首。唯一的例外是当数量 想要分配的内存太大了,例如:一个数组 被分配的内存大于最大内存,那么你知道 一些计算真的出错了,比如 32000x32000@4 图像 占用大约 4GB 内存。

            如果您可以重现:获取堆转储并分析您的应用的使用情况。 正常OOM诊断步骤:

            • 重现异常 (等到你在 LogCat 中看到它)

            • 进行堆转储(分析内存泄漏)

            • 分析大对象和泄漏

            在上面的共享链接中,关于how to take heap dump?issues 的其他链接很少与这个相同。

            所以我建议你分析内存泄漏并采取必要的措施来防止OOM。

            希望这会对你有所帮助。

            【讨论】:

              【解决方案10】:

              也许可以采取不同的方法来解决这个问题。 为此,您可以使用不同的 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 methods to check memory and bitmapgithub discussion

              【讨论】:

                【解决方案11】:

                我遇到了同样的问题,并通过在我的清单文件的应用程序标记中添加android:largeHeap="true" 来解决它,如下所示。

                <manifest>
                   ...
                   <application
                    .....
                    android:largeHeap="true"
                    ....
                   >
                   ....
                   </application>
                </manifest>
                

                注意:这应该是您最后的选择,因为不建议使用 largeHeap:true 来解决简单的 OOM 问题。

                【讨论】:

                  猜你喜欢
                  • 2014-01-11
                  • 1970-01-01
                  • 2016-01-06
                  • 2012-12-30
                  • 1970-01-01
                  • 2011-06-18
                  • 1970-01-01
                  相关资源
                  最近更新 更多