INTRODUCTION:
I work on an android app which has a lot of images. These images are displayed within several recycler views and view pagers. For caching the images I use Fresco which is initialised like this in Application class:
Fresco.initialize(this, ImagePipelineConfig
.newBuilder(getApplicationContext())
.setBitmapMemoryCacheParamsSupplier(new BitmapMemoryCacheParamsSupplier(getApplicationContext()))
.setDownsampleEnabled(true)
.build());
When images are displayed within RecyclerView I use SimpleDraweeView and are loaded like this:
val imageRequest = ImageRequestBuilder
.newBuilderWithSource(Uri.parse(imageUrl))
.setResizeOptions(ResizeOptions(50, 10))
.build()
(itemView as SimpleDraweeView).setImageRequest(imageRequest)
Should also be noted that for each request in order to avoid memory caching the images the following line is called
Fresco.getImagePipeline().evictFromMemoryCache(Uri.parse(imageUrl))
I also load images within view pagers where I use ZoomableDraweeView need also for zooming in, for those cases images are loaded like this:
val view = ZoomableDraweeView(container.context)
view.setAllowTouchInterceptionWhileZoomed(true)
view.controller = Fresco.newDraweeControllerBuilder()
.setImageRequest(ImageRequestBuilder.newBuilderWithSource(Uri.parse(imageUrls[position]))
.setResizeOptions(ResizeOptions(50, 20)).build()).build()
Also for these requests I avoid using the memory caching and I use
Fresco.getImagePipeline().evictFromMemoryCache(Uri.parse(imageUrls[position]))
As well should be noted that each time the user leaves these Activities that contain images I have this:
#Override
protected void onStop() {
super.onStop();
Fresco.getImagePipeline().clearMemoryCaches();
}
THE PROBLEM:
The issue is that on some devices at some point while scrolling through these images my app crashes with Out Of Memory
QUESTION
Is there any way I could improve the Fresco setup, or is there anything I'm missing that could help me get rid of OOM ?
UPADTE WITH CRASH LOG:
java.lang.OutOfMemoryError: Failed to allocate a 2833932 byte allocation with 1972896 free bytes and 1926KB until OOM
E at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
E at android.graphics.Bitmap.nativeCreate(Native Method)
E at android.graphics.Bitmap.createBitmap(Bitmap.java:975)
E at android.graphics.Bitmap.createBitmap(Bitmap.java:946)
E at android.graphics.Bitmap.createBitmap(Bitmap.java:913)
E at android.graphics.drawable.VectorDrawable$VectorDrawableState.createCachedBitmapIfNeeded(VectorDrawable.java:834)
E at android.graphics.drawable.VectorDrawable.draw(VectorDrawable.java:318)
E at android.graphics.drawable.LayerDrawable.draw(LayerDrawable.java:916)
E at com.facebook.drawee.drawable.ForwardingDrawable.draw(ForwardingDrawable.java:185)
E at com.facebook.drawee.drawable.ScaleTypeDrawable.draw(ScaleTypeDrawable.java:123)
E at com.facebook.drawee.drawable.FadeDrawable.drawDrawableWithAlpha(FadeDrawable.java:302)
E at com.facebook.drawee.drawable.FadeDrawable.draw(FadeDrawable.java:289)
E at com.facebook.drawee.drawable.ForwardingDrawable.draw(ForwardingDrawable.java:185)
E at com.facebook.drawee.generic.RootDrawable.draw(RootDrawable.java:81)
Related
I've a custom class which extends TextureView. In this TextureView I've a surfaceTexture, mSurfaceTexture. This is a single-buffered surface.
As per android documentation, here:
In single buffered mode the application is responsible for serializing access to the image content buffer. Each time the image content is to be updated, the releaseTexImage() method must be called before the image content producer takes ownership of the buffer.
So I'm calling this whenever the image producer is taking ownership. It works fine on Android 10, however on android 9 I get the following error:
11-05 19:05:53.960 23442 24509 E AndroidRuntime: java.lang.IllegalStateException: Unable to release texture contents (see logcat for details)
11-05 19:05:53.960 23442 24509 E AndroidRuntime: at android.graphics.SurfaceTexture.nativeReleaseTexImage(Native Method)
11-05 19:05:53.960 23442 24509 E AndroidRuntime: at android.graphics.SurfaceTexture.releaseTexImage(SurfaceTexture.java:252)
I'm calling ANativeWindow_fromSurface. After using this should I do something else too like release nativewindow etc?
Any idea as to why this is happening and also anyone else had a similar issue ?
The relevant code:
runOnUiThread(new Runnable() {
#Override
public void run() {
ImageButton btn = (ImageButton) findViewById(R.id.info_image);
btn.setImageResource(currentID[currentIDPos]);
}
});
It's saying that the run method is making a 400-some megabyte allocation. My jpgs are no more than a megabyte in size. The run() method is called on a timer every 3-4 seconds or so and on click, but that shouldn't matter because from what I can tell using printlns the run() is only called once before the crash.
Edit: Here's the exception. Had to run it again to get this, it's a different amount of memory this time but still very large.
FATAL EXCEPTION: main
Process: com.grey.handsaver, PID: 32531
java.lang.OutOfMemoryError: Failed to allocate a 215151564 byte allocation with 33554400 free bytes and 100MB until OOM
at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:609)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:444)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:988)
at android.content.res.Resources.loadDrawableForCookie(Resources.java:2580)
at android.content.res.Resources.loadDrawable(Resources.java:2487)
at android.content.res.Resources.getDrawable(Resources.java:814)
at android.content.res.XResources.getDrawable(XResources.java:572)
at android.content.Context.getDrawable(Context.java:403)
at android.widget.ImageView.resolveUri(ImageView.java:747)
at android.widget.ImageView.setImageResource(ImageView.java:398)
at com.grey.handsaver.ExerciseInfoActivity$3.run(ExerciseInfoActivity.java:128)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5430)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:913)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:706)
at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:115)
Try to use picasso library for loading large image.it's having inbuilt cache functionality.
Step 1:
First write follow code in gradle.build and sync gradle.
compile 'com.squareup.picasso:picasso:2.5.2'
Step 2:
Picasso.with(context).load("YOUR IMAGE PATH").into(btn);
and write above code where we load the image.in this code you must need to provide image path and make sure image path is valid.if you really want to learn something more about this Library than please Visit this.
I hope your are clear with my Idea.
Best of Luck
Most likely the issue is that you are not clearing up the previously allocated space. Are you sure that the fragment/activity that loads these pictures is also removed from backstack? Or maybe the images themselves are not being garbage collected?
Re-do the same steps that are causing the OutofMemory Exception but make sure to switch to Memory Monitor Tab in Android Studio and check where the Memory is increasing drastically but not being freed when it should be. This should give you a head start to see where the memory leak might be.
Also possible that there is no leak, rather your pictures themselves are huge (although doesnt seems to be). In this case you should do lazy loading, cache implementation etc. or just use some third party library like Picasso which handle this for you
Alright, I figured it out. I'm using an lg g4, which uses xxxhdpi, and my images were in the drawable folder, not the drawable-xxxhdpi folder. This solved my problem without using picasso or the other methods.
i have an android app with 3 acitivtys:
A1 --starts--> A2 --starts--> A3 --when finished his process: starts--> A1
(so i don't "finish();" an app. i start the next activitys with "startActivity(..);" the whole time after userinteraction)
so there is a loop in these 3 activitys.
On each Activity, i display 3-9 pictures, located on the SD-card, which i load with my following function:
try
{
Uri selectedImageURI = Uri.parse(strImagePath);
File imgFile = new File(getRealPathFromURI(selectedImageURI, c));
Bitmap myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
ivTmp.setImageBitmap(myBitmap);
}catch (Exception e)
{
return null;
}
This all works.
But sometimes (after looping a few times through my activitys), my app crashes..
Logcat tells me:
01-16 13:42:15.863: DEBUG/dalvikvm(23161): GC_BEFORE_OOM freed 10K, 9% free 59019K/64400K, paused 29ms, total 30ms
01-16 13:42:15.863: ERROR/dalvikvm-heap(23161): Out of memory on a 8018704-byte allocation.
01-16 13:42:15.863: ERROR/AndroidRuntime(23161): FATAL EXCEPTION: main
java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:502)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:355)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:785)
at android.content.res.Resources.loadDrawable(Resources.java:1965)
at android.content.res.Resources.getDrawable(Resources.java:660)
at android.widget.ImageView.resolveUri(ImageView.java:616)
at android.widget.ImageView.setImageResource(ImageView.java:349)
at <MyApp>.MyActivity$6.run(MyActivity.java:143)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5039)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
at dalvik.system.NativeStart.main(Native Method)
Someone can give my some tips how to handle the crashes?
Maybe its because my activitys are set to state "paused" instead of closing them correctly?
for quick fix you can add
android:largeHeap="true" in your manifest application tag
link here:
I face OOM problem for my table in kindle and nook
Heap size (large)
android:largeHeap="true"
there specification is mentioned to use larger heap link here:
edit: use Caching Bitmaps technique link here
I had the similar problem while displaying high resolution images. I have searched and applied all the android bitmap solutions given in http://developer.android.com/training/displaying-bitmaps/index.html and the following caches mekanism in the link. but none of them work. not any right solution anywhere. Then I figured out that the problem is : I couldnt use the drawable folder structure right. I was keeping high resolution images in mdpi and hdpi folders . this cause android scale up the images to the ultra sizes. 100 kb image was causing 20 mb increase in memory thanks to the android monitor / memory section.
so I have added these ultra resolution images to xxhdpi folder , then It was FIXED suddenly. then my image slider work flawlessly
Yeah android doesnt immediately destroy activities when they are not visible to the user, but there are a number of lifecycle methods that are called depending on the current state. Check out http://developer.android.com/guide/components/activities.html#Lifecycle
You could try finishing the activities in onStop() or onDestroy() as these will be called when the activity is not visible and when the system runs low on memory respectively. :-)
This is due to the high resolution of image you have to scaling the bitmap use the following code
Bitmap myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
int h = 100; // height in pixels
int w = 100; // width in pixels
Bitmap photoBitMap = Bitmap.createScaledBitmap(myBitmap,h, w, true);
ivTmp.setImageBitmap(photoBitMap);
In our app we have a lot of images. When they needed we get them in
such way: PlayN.assets().getImage("imageURL");
In android 2.3 there is limit for 24mb of bitmap resources. So, while
showing images on Screen2(let's say Game) i must hide (unload) images
from Screen1(lets say Intro).
I have reference to image from Screen1 and i can call clear() method:
CanvaseImage referenceToImage - i need to unload,
referenceToImage.canvas().clear();
BUT, - this way do not unload image from android memory in 2.3.x -
( Android: Bitmap recycle() how does it work?...
this post tell that i should call recycle() on bitmap, since bitmaps
are stored in non-management memory and cant be shrinked from there by
GC.)
and i have such stackTrace for android if images from Screen1 are not
unloaded from memory:
06-11 09:16:45.197: E/AndroidRuntime(23621): FATAL EXCEPTION: GLThread
10
06-11 09:16:45.197: E/AndroidRuntime(23621):
java.lang.OutOfMemoryError: bitmap size exceeds VM budget(Heap
Size=9479KB, Allocated=6032KB, Bitmap Size=23347KB)
06-11 09:16:45.197: E/AndroidRuntime(23621): at
android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
06-11 09:16:45.197: E/AndroidRuntime(23621): at
android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:694)
06-11 09:16:45.197: E/AndroidRuntime(23621): at
playn.android.AndroidAssets.decodeBitmap(AndroidAssets.java:158)
06-11 09:16:45.197: E/AndroidRuntime(23621): at
playn.android.AndroidAssets.doGetImage(AndroidAssets.java:79)
06-11 09:16:45.197: E/AndroidRuntime(23621): at
playn.core.AbstractAssets.getImage(AbstractAssets.java:39)
Is there some way to call playN 's magic for recycling bitmaps in
Android 2.3.x memory model?
Have you already taken a look at the Google article about memory managment? They really give a fairly in depth explanation of how to deal with that bitmap issue.
why don't you use LruCache for caching bitmaps? see article here
I had a user comment that after viewing a bunch of images in my app, it crashes (he believes that it is due to out of memory error). I have the following relevant code:
int themeID = mNav[mPos];
String icon = getThemeData(DbAdapter.KEY_ICON, themeID);
ImageView viewer = (ImageView)findViewById(R.id.viewer);
Bitmap bMap = null;
try {
bMap = getJPG(icon + ".jpg");
} catch (IOException e) {
e.printStackTrace();
}
viewer.setImageBitmap(bMap);
That gets reran as the user flips between images. From here I see that you should call recycle() on bitmaps. Do i need to call it on bMap after setting the image? Or is there some way to pull it from viwer prior to setting the next one?
According to the documentation for recycle (if I call it on bMap) it appears I don't need to use it: This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.
If you need to explicitly call recycle() it probably means that you have memory leak. Calling it is almost never a solution.
Did you try to check your app for potential mmory leak?
To check it you can for example rotate your device a few times and check how the Garbage Collector behaves. You should have something like GC_... freed 211K, 71% free 300K/1024K, external 0K/0K, paused 1ms+1ms in your LogCat nearly every time you rotate. Watch for changes in this part: 300K/1024K. If you don't have memory leaks, the first part should grow and then get smaller after a few GCs. If you have a memory leak, it will grow and grow, to the point of OOM error.
Check out my other answer about a memory leak.
If you're sure you don't have a leak and you're operating on Honeycomb you can increase the heap size accessible for your app like this: android:largeHeap="true" but it's only recommended when you deal with some huuuge bitmaps or videos, so don't overuse it.