[英]Out Of Memory Error When loading more images in Glide
Edited:编辑:
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.祝你好运。
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.我正在使用
glide
和recyclerView
,我正在加载 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_image
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_image
和golive_cancel_image
文件放入该文件夹中,然后从其他位置删除这两个图像文件,例如drawable-ldpi
、 drawable-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);
.thumbnail(...f)
).thumbnail(...f)
).skipMemoryCache(true)
if you are not force to keep images in cache.skipMemoryCache(true)
.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.