简体   繁体   English

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

[英]Out Of Memory Error When loading more images in Glide

Edited:编辑:

  • In my application, I am loading more than 300 images in home page.在我的应用程序中,我在主页中加载了 300 多个图像。 I used glide to load images.我使用glide来加载图像。 I'm getting Out of Memory Error .我得到Out of Memory Error

I have used large heap true in manifest :我在manifest 中使用了大堆 true :

android:largeHeap="true"

Glide Version:滑翔版:

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

Device/Android Version:设备/安卓版本:

Nexus Device 6.0 version Nexus 设备 6.0 版本

Every images I'm getting from Json would be 800kb to 1mb.我从 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>

adapter code:适配器代码:

@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:适配器布局.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)

I don't know how to fix this OOM issue.我不知道如何解决这个 OOM 问题。 Please share your suggestion, if you have already familiar with this issue.如果您已经熟悉这个问题,请分享您的建议。

  • I solved this issue by removing nested scroll view placed above recyclerview.我通过删除放置在 recyclerview 上方的嵌套滚动视图解决了这个问题。 Why OutOfMemory error occurred means, when loading more than 200 images in home page, it is loading all 200 images because of using nested scroll view above recyclerview.为什么出现 OutOfMemory 错误意味着,当在主页中加载超过 200 张图片时,由于使用了 recyclerview 上方的嵌套滚动视图,它正在加载所有 200 张图片。

  • So I can't check the logcat image view width and height one by one in adapter.所以我无法在适配器中一一检查logcat图像视图的宽度和高度。

  • After removed nested scroll view fixed out of memory error.because it will load only 3 images displayed in device when coming to home activity.删除嵌套滚动视图后,修复了内存不足错误。因为当回家活动时,它只会加载设备中显示的 3 个图像。

  • Also check this , how to use scroll instead of nested scroll view.还要检查这个,如何使用滚动而不是嵌套滚动视图。

This is not an exact solution to your problem, but you need to keep these things in mind while loading images in a list using Glide.这不是您问题的确切解决方案,但您需要在使用 Glide 加载列表中的图像时记住这些事情。

The main threatening part of your problem is the image size.问题的主要威胁部分是图像大小。 The image you're getting is almost 1mb each!你得到的图像每个几乎 1mb! Which is in fact too large for displaying them into a list having 300+ items.这实际上太大了,无法将它们显示到包含 300 多个项目的列表中。 So if you're doing the server side too, its always recommended to have the images in several different sizes.所以如果你也在做服务器端,总是建议使用几种不同大小的图像。

For example, in case of showing a friend list along with their profile pictures, I would suggest you get the whole list first from the server.例如,如果显示好友列表及其个人资料图片,我建议您首先从服务器获取整个列表。 Then fetch all of the profile images and store them locally.然后获取所有个人资料图像并将它们存储在本地。 Then populate the ListView .然后填充ListView And the most important part is while uploading a profile picture of an user to the server, after uploading it, the server needs to keep several sizes of it eg low, mid and high res version.最重要的部分是在上传用户的个人资料图片到服务器时,上传后,服务器需要保留几个尺寸,例如低、中、高分辨率版本。 So that while serving the profile picture urls for the ListView the server might provide the images with low res as they'll be used most likely for thumbnails.因此,在为ListView提供个人资料图片 url 时,服务器可能会提供低分辨率的图像,因为它们最有可能用于缩略图。

Using RecyclerView instead of ListView is a good call too.使用RecyclerView而不是ListView也是一个不错的选择。 But it won't solve the problem you've here when you're in a low-end device.但是当您使用低端设备时,它不会解决您在这里的问题。

OMM has nothing to do with you can solve programatically. OMM与您可以通过编程方式解决无关。 You need to resize your image to a lower res version.您需要将图像大小调整为较低分辨率的版本。

You can check for the Glide's caching mechanism too.您也可以检查 Glide 的缓存机制 I would suggest you use the caching strategy so that every time you don't have to load the image from server.我建议您使用缓存策略,这样每次都不必从服务器加载图像。

Good luck.祝你好运。

  1. Make sure the ImageView has match_parent or fixed dp as dimensions wrap_content makes Glide load full resolution Bitmaps.确保 ImageView 具有 match_parent 或固定 dp 作为尺寸 wrap_content 使 Glide 加载全分辨率位图。
  2. .placeholder() shows an image instead of empty space while loading large bitmap .placeholder() 在加载大位图时显示图像而不是空白区域
  3. .thumbnail(float) loads a downsampled version fast while the bigger image is loading in the background .thumbnail(float) 在后台加载更大的图像时快速加载下采样版本
  4. Also look around the Glide issues , maybe you find something helpful.也看看周围的Glide 问题,也许你会发现一些有用的东西。

Using Glide doesn't guarantee no Out of Memory errors, you need to use several small steps to reduce the probability to not get OOM's .使用 Glide 并不能保证没有Out of Memory错误,你需要用几个小步骤来降低不得到OOM's概率。

Step 1: Understand the caching mechanism in Glide第一步:理解 Glide 中的缓存机制

Step 2: I prefer to load thumbnails into recyclerview第 2 步:我更喜欢将thumbnails加载到 recyclerview

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

Remember to always request small size image if bigger or HD images are not required.如果不需要更大或高清图像,请记住始终请求小尺寸图像。

Use recyclerView instead of ListView .使用recyclerView而不是ListView It reusable single item for rendering items.它可重复使用单个项目来渲染项目。 I am using glide with recyclerView where i am loading wallpapers with 100+ items.我正在使用gliderecyclerView ,我正在加载 100 多个项目的壁纸。

In ListView every time you are creating view, if you have 100+ view and it will create 100+ views where as in recyclerview it creates how many visible items are there in screen +2.在每次创建视图时在 ListView 中,如果您有 100 多个视图,它将创建 100 多个视图,而在 recyclerview 中,它会创建屏幕 +2 中有多少可见项目。

The reason is while scrolling, glide keep doing image process even related views removes from list.原因是在滚动时,即使相关视图从列表中删除,glide 也会继续进行图像处理。 Add this code in your listview's onScrollStateChanged.将此代码添加到您的列表视图的 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;
        }
    }

I faced the similar problem .我遇到了类似的问题。 I am sharing the way I solved it .我正在分享我解决它的方式。 Create a folder named drawable-nodpi put your golive_load_image and golive_cancel_im‌​age file into that folder , and remove those two image file from other place like drawable-ldpi , drawable-hdpi etc (if you have there ) .创建一个名为drawable-nodpi文件夹,将您的golive_load_imagegolive_cancel_im‌​age文件放入该文件夹中,然后从其他位置删除这两个图像文件,例如drawable-ldpidrawable-hdpi等(如果有的话)。 And add skipMemoryCache( true )并添加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);
  • your images should not be too large ( if they are, use .thumbnail(...f) )您的图像不应太大(如果是,请使用.thumbnail(...f)
  • use .skipMemoryCache(true) if you are not force to keep images in cache如果您不强制将图像保留在缓存中,请使用.skipMemoryCache(true)
  • you can use .diskCacheStrategy(DiskCacheStrategy.NONE) to deactivate the disk cache您可以使用.diskCacheStrategy(DiskCacheStrategy.NONE)来停用磁盘缓存

In order to prevent Out of Memory error one can just make precautions to make sure that it will not occurs.为了防止 Out of Memory 错误,您可以采取预防措施以确保它不会发生。 So the answer of this question is actually a bunch of suggestions one can suggest.所以这个问题的答案实际上是一堆可以提出的建议。 So do I.我也是。

As suggested by @Reaz Murshed I am also recommending to have the images in several different sizes.正如@Reaz Murshed所建议的,我还建议使用几种不同尺寸的图像。 Apart from this I would like to add few more things that might help you analyze this issue and solve it.除此之外,我想添加一些可能有助于您分析此问题并解决它的内容。

As far as I remember OOM was always a usage error, largeHeap will just delay it;据我所知,OOM 总是一个使用错误, largeHeap只会延迟它; or if it's a large load then maybe it's not possible.或者如果它是一个大负载,那么它可能是不可能的。 So I am suggesting you to follow this link to diagnose for memory leaks.所以我建议你按照这个链接来诊断内存泄漏。

Stack traces of OutOfMemoryErrors don't help at all for diagnosing them. OutOfMemoryErrors堆栈跟踪对诊断它们根本没有帮助。 It just tells you it's broken and something filled up the memory.它只是告诉你它坏了,有些东西填满了内存。 This filling happens much before the actual exception was thrown.这种填充发生在实际异常被抛出之前。 This also implies that usually whatever throws the OOM is not actually the culprit.这也意味着通常抛出OOM任何东西实际上并不是罪魁祸首。 The only exception to this is when the amount of wannabe allocated memory is simply too big, for example: an array to be allocated is bigger than the maximum memory, then you know that some calculation went really wrong, like a 32000x32000@4 image would take around 4GB of memory.唯一的例外是当想要分配的内存量太大时,例如:要分配的数组大于最大内存,那么您知道某些计算确实出错了,例如 32000x32000@4 图像会占用大约 4GB 的内存。

If you can reproduce: get a heap dump and analyze your app's usage.如果您可以重现:获取堆转储并分析您的应用程序的使用情况。 Normal OOM diagnostic steps:正常的OOM诊断步骤:

  • Reproduce exception (wait till you see it in LogCat)重现异常(等到你在 LogCat 中看到它)

  • Take a heap dump(To analyze memory leaks)进行堆转储(分析内存泄漏)

  • Analyze it for big objects and leaks分析大物体和泄漏

In above shared link there is few another links regarding how to take heap dump?在上面的共享链接中,关于how to take heap dump?其他链接很少how to take heap dump? and issues that are identical with this one.以及与此相同的问题

So I suggest you to analyze for memory leaks and take necessary steps to prevent OOM.所以我建议你分析内存泄漏并采取必要的措施来防止 OOM。

Hope this will help you.希望这会帮助你。

Probably a different approach can be taken to resolve this.可能可以采取不同的方法来解决这个问题。 To achieve this you can use a different ImageAdapter with为此,您可以使用不同的ImageAdapter

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

this does not crash being using MediaStoreThumbFetcher使用MediaStoreThumbFetcher不会崩溃

To have more control over the load do the following using Glide v4要更好地控制负载,请使用 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);
    }
}

Using following API's we can figure out free memory left out and size of the bitmap使用以下 API,我们可以计算出剩余的可用内存和位图的大小

You can check available memory and bitmap details (if needed) as a pre-check您可以检查可用内存和位图详细信息(如果需要)作为预检查

Check the amount of free memory left检查剩余的可用内存量

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

Check how big is the bitmap we want to load检查我们要加载的位图有多大

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

For more details go through Java methods to check memory and bitmap and github discussion有关更多详细信息,请通过Java 方法检查内存和位图以及github 讨论

I faced the same issue and solved it by adding android:largeHeap="true" in the application tag of my manifest file like below.我遇到了同样的问题,并通过在清单文件的应用程序标签中添加android:largeHeap="true"来解决它,如下所示。

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

NB: this should be your last option, as using largeHeap:true is not recommended for solving simple OOM problems.注意:这应该是您最后的选择,因为不建议使用largeHeap:true来解决简单的 OOM 问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM