In the documentation, titled "Managing Bitmap Memory" you can find the following statement:
Caution: You should use recycle() only when you are sure that the bitmap is no longer being used. If you call recycle() and later attempt to draw the bitmap, you will get the error: "Canvas: trying to use a recycled bitmap".
So, what exactly means "no longer"?
I call setImageDrawable(drawable) in my fragments onCreateView(...) method. And I call recycle() on the drawable's bitmap in the fragment's onStop().
When the user now leaves the fragment by launching another activity the bitmap is recycled. But when the user comes back to the previous fragment, its onCreateView() is called again, resulting in a new call to "setImageDrawable(drawable)". And this throws:
IllegalArgumentException: Cannot draw recycled bitmaps
So, I still seem to be in the "no longer" context. When do I get a new bitmap, which is not recycled? Only after the fragment and its activity have been completely destroyed?
So, what exactly means "no longer"?
No longer means you are not going to use the same reference of Bitmap.
As you told you are recycling bitmaps on onstop(), try with this also inside your onStop()
if(bitmap!=null)
{
bitmap.recycle();
bitmap=null;
}
Bitmap and outOfMemory in android
Watch first 20 minuts of this official video if you want to make your day good - http://www.youtube.com/watch?v=_CruQY55HOk
Related
Hello I am trying to get bitmap from URL in the Fragment. I know programmer should recycle the bitmap to release the memory and avoid OutOfMemoryException.
So here I am very confused where I should call recycle method in Fragment and Activity, I see many answer but that didn't seems to me any nice answer like android fragment: recycle bitmap
Can you please help me to clear my confusion.
Thanks in advance
call the recycle method in onDestroy() if you finish using it when the activity / fragment is destroyed. Otherwise recycle it as soon as you are finished using it
Disclaimer: I read about 20 existing questions. Unfortunately none of them solved my problem.
I have an activity. This activity gets a Bitmap from a global/static class C and sets the bitmap to an ImageView. When the activity is finished and I try to reopen the activity, I get the java.lang.RuntimeException: Canvas: trying to use a recycled bitmap exception. I don't call Bitmap.recycle() at any point. Also, I do not delete the bitmap reference in class C. I tried the following but they didn't work:
Call ImageView.setDrawingCacheEnabled(false)
Before setting the bitmap for ImageView, make a deep copy (via Bitmap.copy() method)
Also, I put some log statements throughout the activity lifecycle. Inside onDestroy(), before calling super.onDestroy() I check and make sure that the bitmap is not recycled (through Bitmap.isRecycled())
How can I fix this issue?
The culprit turned out to be RemoteControlClient.MetadataEditor.apply(), it does call recycle() for bitmaps passed to it.
I had similar issue with ImageViews when reopening the activity (e.g. after screen is waked). The problem lies not in Bitmap itself, but in ImageView. My solution was to set ImageView's setImageBitmap to null on activity's onPause() and call setImageBitmap(theBitmap) on activity's onResume().
I have a ViewPager with a couple of fragments. In a fragment onCreateView,
I decode a couple of bitmaps that are assigned to fields in the Fragment class. If I try to swipe between the fragments, at a certain point the application gets an OutOfMemory exception (heap is exhausted).
Ok, this is a really bad practice, but, isn't the GC supposed to free memory before my application is killed?
Reassigning the Bitmap to the same field should cause the previous Bitmap to be released, am I wrong? On S4 I get the exception very soon.
Suppose you load a bitmap, lets say that is the first and then assign a new decoded second bitmap to the first, the first bitmap is not GC'ed when you decode the second one. GC will do it later whenever it decides. If you want to free memory ASAP you should call recycle() just before decoding the second bitmap. Src: here. And, go through Android's Managing Bitmap Memory article. Refer this too.
Fragments are kept in memory unless stated otherwise, so either you manually detach and dispose of fragments when swiping or mBitmap.recycle() when swiping.
Edit, code:
final FragmentTransaction fm = getActivity()
.getSupportFragmentManager().beginTransaction();
fm.replace(R.id.fragPlayerMain, playerFragment, "fragment").addToBackStack(null);
fm.hide(thisFrag);
fm.detach(thisFrag);
fm.commitAllowingStateLoss();
In my APP, Activity-A triggers another Activity-B. Activity-B triggers another Activity-C. Now the control return to the Activity-A from Activity-C Via Activity-B. The Activity-A uses the Bitmap image to draw as a background in its Canvas.
Question:
When I move from Activity-A to B, Do I need to recycle the bitmap variable (eg. background.recycle()) in onPause() method ?
Assume, I recycled the bitmap variable in onPause() method. Will this be a good approach to avoid OOM error ( keep in mind that the image has to be re-drawn when it comes back to Activity-A)
Do you get OOM errors? If not (since your bitmap is not big) then simply do nothing ... The docu says about the recycle method:
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.
So there is also a main difference between Android 2.x and Android 4.x. In Android 4 the bitmap memory is no longer managed natively, but is part of the normal java heap and works like every reference works: If the there is no reference anymore, then the garbage collector will collect the bitmap somewhere in the future.
Wherer is the right point to call recycle? Well the right point will be, when the Bitmap is no longer displayed on screen. so onPause could be a possibility, but keep in mind that you have to reload it (async) in onResume. So whats the problem with that approach? Once you have marked the Bitmap to be no longer needed by calling recycle() the bitmap is no longer useable, but probably has not been GC collected yet. So if the user jumps from Activty A to B and will return quickly, it's possible, that you have the same bitmap twice in memory, because the GC has not collected yet the first (recycled) bitmap.
So my tipp is: Try to reduce the bitmap size if you have memory issues. Use less quality bitmap (have a look at Bitmap Options). But I guess you need to try diffrent strategies to find the best working one for your app.
every time you don't need the bitmap recycle it. using
if method at least to avoid force closing in some cases.
if (null != bmp){
bmp.recycle();
}
and i advice you instead of sending the Bitmap between activities get its filename by using intent and pass it through your activities like this you will avoid re-drawing the bitmap in every activity and that will cause OOM for sure.
Im clearing up the bitmaps i load in one activity before i more into the other activity.
eg:
pic1 = null;
System.gc();
nextActivityIntent = new Intent(ThisActivity,NextActivity.class);
ThisActivity.startActivityForResult(nextActivityIntent,123);
But the problem is system calls onDraw some times after i call "pic1=null". When it happens the application crashes with pointing a NullPoint Exception.
Cab any one suggest me how to stop calling onDraw() after setting the "pic1=null". Can i use synchornized to make this happen.
You can try view.setWillNotDraw(true); (link here), but I'm not sure it will prevent it.
On a side note, if your first activity will finish, there is no need to set the bitmap to null nor to call System.gc();. The bitmaps will be recovered anyway after the activity is destroyed.
If for some reason you still want to do it, you should do that inside the onDestroy() callback to avoid any drawing issues. Check the Activity lifecycle for more details.