问题
Edited:
- In my application, I am loading more than 300 images in home page. I used
glide
to load images. I'm gettingOut of Memory Error
.
I have used large heap true in manifest :
android:largeHeap="true"
Glide Version:
compile 'com.github.bumptech.glide:glide:3.7.0'
Device/Android Version:
Nexus Device 6.0 version
Every images I'm getting from Json would be 800kb to 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:
<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. Please share your suggestion, if you have already familiar with this issue.
回答1:
I solved this issue by removing nested scroll view placed above 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.
So I can't check the logcat image view width and height one by one in adapter.
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.
Also check this, how to use scroll instead of nested scroll view.
回答2:
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.
The main threatening part of your problem is the image size. The image you're getting is almost 1mb each! Which is in fact too large for displaying them into a list having 300+ items. 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
. 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 e.g. 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.
Using RecyclerView
instead of ListView
is a good call too. 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. You need to resize your image to a lower res version.
You can check for the Glide's caching mechanism too. I would suggest you use the caching strategy so that every time you don't have to load the image from server.
Good luck.
回答3:
- Make sure the ImageView has match_parent or fixed dp as dimensions wrap_content makes Glide load full resolution Bitmaps.
- .placeholder() shows an image instead of empty space while loading large bitmap
- .thumbnail(float) loads a downsampled version fast while the bigger image is loading in the background
- Also look around the Glide issues, maybe you find something helpful.
回答4:
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
.
Step 1: Understand the caching mechanism in Glide
Step 2: I prefer to load thumbnails
into 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.
回答5:
Use recyclerView
instead of ListView
. It reusable single item for rendering items. I am using glide
with recyclerView
where i am loading wallpapers with 100+ items.
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.
回答6:
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 ) . And add 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);
回答7:
The reason is while scrolling, glide keep doing image process even related views removes from list. Add this code in your listview's 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;
}
}
回答8:
- your images should not be too large ( if they are, use
.thumbnail(...f)
) - use
.skipMemoryCache(true)
if you are not force to keep images in cache - you can use
.diskCacheStrategy(DiskCacheStrategy.NONE)
to deactivate the disk cache
回答9:
In order to prevent Out of Memory error one can just make precautions to make sure that it will not occurs. 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. 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; 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. 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 theOOM
is not actually the culprit. 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.If you can reproduce: get a heap dump and analyze your app's usage. Normal OOM diagnostic steps:
Reproduce exception (wait till you see it in 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?
and issues that are identical with this one.
So I suggest you to analyze for memory leaks and take necessary steps to prevent OOM.
Hope this will help you.
回答10:
Probably a different approach can be taken to resolve this. To achieve this you can use a different ImageAdapter with
Glide.with(mActivity).loadFromMediaStore(_imageInfo.getmUri())
this does not crash being using MediaStoreThumbFetcher
To have more control over the load do the following using 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
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
来源:https://stackoverflow.com/questions/38560637/out-of-memory-error-when-loading-more-images-in-glide