内存不足错误在Glide中加载更多图像时

时间:2016-07-25 05:59:25

标签: java android out-of-memory

编辑:

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

我在清单

中使用了大堆true
android:largeHeap="true"

滑翔版:

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

设备/ Android版本:

Nexus Device 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问题。如果您已经熟悉此问题,请分享您的建议。

11 个答案:

答案 0 :(得分:9)

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

问题的主要威胁部分是图像大小。你得到的图像几乎都是1mb!实际上,将它们显示为具有300多个项目的列表实际上太大了。因此,如果您也在服务器端,它总是建议使用几种不同大小的图像。

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

使用ListView代替OMM也是一个不错的电话。但当你进入低端设备时,它不会解决你在这里遇到的问题。

validateCode()与您无关可以以编程方式解决。您需要将图像调整为较低版本。

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

祝你好运。

答案 1 :(得分:9)

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

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

  • 删除嵌套滚动视图后修复了内存错误。因为它 来家时只会加载设备中显示的3张图像 活性。

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

答案 2 :(得分:4)

  1. 确保ImageView具有match_parent或固定dp作为尺寸 wrap_content使Glide加载全分辨率位图。
  2. .placeholder()在加载大位图时显示图像而不是空白
  3. .thumbnail(float)快速加载下采样版本,而较大的图像在后台加载
  4. 还可以查看Glide issues,也许您会发现有用的内容。

答案 3 :(得分:4)

使用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 :(得分:3)

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

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

答案 5 :(得分:2)

我遇到了类似的问题。我正在分享我解决它的方式。创建一个名为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);

答案 6 :(得分:1)

原因是在滚动时,滑动继续进行图像处理甚至相关视图从列表中删除。在listview的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 :(得分:1)

  • 您的图片不应太大(如果是,请使用.thumbnail(...f)
  • 如果您没有强制将图像保留在缓存中,请使用.skipMemoryCache(true)
  • 您可以使用.diskCacheStrategy(DiskCacheStrategy.NONE)停用磁盘缓存

答案 8 :(得分:1)

为了防止Out of Memory错误,可以采取预防措施以确保不会发生。所以这个问题的答案实际上是一堆可以暗示的建议。我也是。

根据 @Reaz Murshed 的建议,我也建议将图像分成几种不同的尺寸。除此之外,我想补充一些可能有助于你分析这个问题并解决它的事情。

据我记得,OOM始终是一个使用错误,largeHeap只会延迟它;或者如果它是一个很大的负载,那么也许这是不可能的。因此,我建议您按照this链接进行内存泄漏诊断。

  

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

     

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

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

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

  •   
  • 分析大对象和泄漏

  •   

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

因此,我建议您分析内存泄漏并采取必要措施来防止OOM。

希望这会对你有所帮助。

答案 9 :(得分:1)

可能采取不同的方法来解决这个问题。 要实现此目的,您可以使用不同的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

答案 10 :(得分:0)

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

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

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