I just read a blogpost by Romain Guy on how to avoid memory leaks in Android.
In the article he gives this example:
private static Drawable sBackground;
#Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);
setContentView(label);
}
Romain said:
This example is one of the simplest cases of leaking the Context.
My question is, how do you modify it correctly?
Just like this?
TextView label = new TextView(Context.getApplicationContext());
I tested both ways and the results are the same. I can't locate the difference. And I think that this is more correct than the Application context. Because this is a reference to Activity, that is to say, the TextView belongs to that Activity.
Could someone give me an explanation for this?
The actual problem with that code isn't the context passed to create the drawable, but private static Drawable sBackground;
The static Drawable is created with the Activity as the context, so in THAT case, there's a static reference to a Drawable that references the Activity, and that's why there's a leak. As long as that reference exists, the Activity will be kept in memory, leaking all of its views.
So it's the Drawable which should be created using the application context, not the TextView. Creating the TextView with "this" is perfectly fine.
edit : Actually, that might not make a big difference, the problem is that once the drawable is binded to a view, there's a reference to the view, which references the activity. So you need to "unbind" the drawable when you exit the activity.
I'm not sure if Romain had updated his blog entry since you read it, but he's pretty clear on how to avoid the leaks, even pointing you to an example in the Android OS. Note that I fixed the broken link in Romain's blog entry via archive.org.
This example is one of the simplest cases of leaking the Context and
you can see how we worked around it in the Home screen's source
code (look for the unbindDrawables() method) by setting the stored
drawables' callbacks to null when the activity is destroyed.
Interestingly enough, there are cases where you can create a chain of
leaked contexts, and they are bad. They make you run out of memory
rather quickly.
There are two easy ways to avoid context-related memory leaks. The
most obvious one is to avoid escaping the context outside of its own
scope. The example above showed the case of a static reference but
inner classes and their implicit reference to the outer class can be
equally dangerous. The second solution is to use the Application
context. This context will live as long as your application is alive
and does not depend on the activities life cycle. If you plan on
keeping long-lived objects that need a context, remember the
application object. You can obtain it easily by calling
Context.getApplicationContext() or Activity.getApplication().
In summary, to avoid context-related memory leaks, remember the
following:
Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the
activity itself)
Try using the context-application instead of a context-activity
Avoid non-static inner classes in an activity if you don't control their life cycle, use a static inner class and make a weak reference to the activity inside. The solution to this issue is to use a static inner class with a WeakReference to the outer class, as done in ViewRoot and its W inner class for instance
A garbage collector is not an insurance against memory leaks
Memory leaks at that code mostly happen when you rotate your screen (that is, changing the orientation state) so your activity was destroyed and created again for the new orientation. There's a lot of explanation about memory leaks.
You can take a look at one of the Google I/O 2011 video about Memory Management here. In the video, you can also use the memory management tools like Memory Analyzer available to download here.
I don't know if you are having any trouble with this in your app, but I have created a drop in solution that fixes all the android memory leak issues with standard android classes: http://code.google.com/p/android/issues/detail?id=8488#c51
public abstract class BetterActivity extends Activity
{
#Override
protected void onResume()
{
System.gc();
super.onResume();
}
#Override
protected void onPause()
{
super.onPause();
System.gc();
}
#Override
public void setContentView(int layoutResID)
{
ViewGroup mainView = (ViewGroup)
LayoutInflater.from(this).inflate(layoutResID, null);
setContentView(mainView);
}
#Override
public void setContentView(View view)
{
super.setContentView(view);
m_contentView = (ViewGroup)view;
}
#Override
public void setContentView(View view, LayoutParams params)
{
super.setContentView(view, params);
m_contentView = (ViewGroup)view;
}
#Override
protected void onDestroy()
{
super.onDestroy();
// Fixes android memory issue 8488 :
// http://code.google.com/p/android/issues/detail?id=8488
nullViewDrawablesRecursive(m_contentView);
m_contentView = null;
System.gc();
}
private void nullViewDrawablesRecursive(View view)
{
if(view != null)
{
try
{
ViewGroup viewGroup = (ViewGroup)view;
int childCount = viewGroup.getChildCount();
for(int index = 0; index < childCount; index++)
{
View child = viewGroup.getChildAt(index);
nullViewDrawablesRecursive(child);
}
}
catch(Exception e)
{
}
nullViewDrawable(view);
}
}
private void nullViewDrawable(View view)
{
try
{
view.setBackgroundDrawable(null);
}
catch(Exception e)
{
}
try
{
ImageView imageView = (ImageView)view;
imageView.setImageDrawable(null);
imageView.setBackgroundDrawable(null);
}
catch(Exception e)
{
}
}
// The top level content view.
private ViewGroup m_contentView = null;
}
Related
I am new to Android,
Is it good to null all objects in ondestroy() method ?
example :
public class HelloAndroid extends Activity {
TextView tv = null;
private static int mValue; // a static member here
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
tv = new TextView(this);
tv.setText((mValue != 0) ?
("Left-over value = " + mValue) : "This is a new instance");
setContentView(tv);
}
public void onDestroy() {
super.onDestroy();
tv = null;
}
}
In theory, it shouldn't have any benefit. However, nulling references in an activity can help mitigate the effects of memory leaks that you otherwise have no control over, such as this one in the Google Maps API.
Whether you should always do this to protect against future memory leaks of this sort is a matter of opinion. You have to weigh the cost of maintaining the extra code against the probably minimal benefit.
It is completely unnecessary. The objects will be garbage collected automatically.
I read a lot about memory leaks in the last few days, and came across some interesting stuff.
I saw this answer to a basic Android bitmap-related memory leak question (the answer is from 2011) and I was wondering if this is still the case.
If I'm using views that contain bitmaps in my activity (ImageViews, TextViews...), do I really need to unbind their drawables when destroying the activity?
Is this only in some cases or always?
It's no longer necessary as of 4.0, as the callback is now stored in a WeakReference.
From 2.3.7:
public final void setCallback(Callback cb) {
mCallback = cb;
}
and in 4.0.1:
public final void setCallback(Callback cb) {
mCallback = new WeakReference<Callback>(cb);
}
Hi so I have a fairly large memory leak in my app and I think it's being caused by my Runnables.
Here is an example of the skeleton of the Runnables i use:
private Runnable randomAlienFire = new Runnable() {
public void run() {
/*A Bunch
of computations
*/
mainHandler.removeCallbacks(randomAlienFire);
mainHandler.postDelayed(randomAlienFire, number );
}
When I switch activities I call mainHandler.removeCallbacksAndMessages(null); and thread.randomAlienFire = null; yet I am still leaking the entire Activity. So my question is, is there something in this basic skeleton that is causing a memory leak? Could it be the fact that the handler is calling to itself?
Yes, your implementation will definitely cause a memory leak (I just ran into this myself).
The problem is that you have created a circular reference. You have declared your runnable as a non-static inner class, which means that it will automatically maintain a reference to the activity. The runnable itself is a member variable of your activity, which closes the circle. The garbage collector will never be able to free these objects since there will always be a living reference.
Using a static inner class with a weak reference to the activity is the safest way to fix the problem. You can see a great code example here. If mainHandler is another non-static inner class, it will create a second circular reference for the same reasons so you will have to do the same thing there.
Setting mainHandler.removeCallbacksAndMessages(null); and thread.randomAlienFire = null; could also work, but you have to be very careful where you put that code. Perhaps the code is taking a different path than you expect in some cases and missing those calls? This blog post describes someone else's very similar experience with that approach.
In my case, I was using a runnable to sequence animations on ImageViews. to get rid of the memory leaks, I created a static runnable class to avoid the circular reference. That alone was not enough for me, I also found that the drawable was still retaining a reference to my fragment. calling myImageView.removeCallbacksAndMessages(arrowAnimationRunnable); in onDestroy() in my fragment finally solved the leak. here was my solution:
public class MyFragment extends SherlockFragment {
public static class SafeRunnable implements Runnable {
private final WeakReference<MyFragment> parentReference;
public SafeRunnable(MyFragment parent) {
parentReference = new WeakReference<MyFragment>(parent);
}
#Override
public void run() {
if (parentReference != null) {
final MyFragment parent = parentReference.get();
if (parent != null) {
runWithParent(parent);
}
}
}
public void runWithParent(MyFragment parent) {
}
}
// This anonymous instance of the new runnable class does not retain a
reference to the fragment
private Runnable arrowAnimationRunnable = new SafeRunnable(this) {
#Override
public void runWithParent(MyFragment parent) {
// ... animation code
// repeat the animation in 1 second
parent.myImageView.postDelayed(this, 1000);
}
};
private ImageView myImageView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.my_layout, container, false);
// find the image view and kick off the animation after 1 second
myImageView = (ImageView) view.findViewById(R.id.iv_arrow);
myImageView.postDelayed(arrowAnimationRunnable, 1000);
return view;
}
#Override
public void onDestroyView() {
super.onDestroyView();
// It's necessary to remove the callbacks here, otherwise a message will
// be sitting in the queue and will outlive the fragment. Because a
// reference in that message will still be pointing to the fragment, the
// fragment (and everything else) will not be garbage collected
myImageView.removeCallbacks(arrowAnimationRunnable);
}
}
By mainHandler.postDelayed(randomAlienFire, number );
you are queueing a task which may have a memory reference. But the activity may become destroyed before the actual works done. That is causing a memory leak for you.
To get rid of this leak, you must call mainHandler.removeCallbacks(randomAlienFire); in an appropriate place before destroying activity. For example if your runnable runs from onStart(), you must call mainHandler.removeCallbacks(randomAlienFire); in onStop();
I am writing a custom view by directly inheriting from the View class, and I am wondering whether I am making a good use of the WeakReference class. First, this is the most relevant part of my class :
public class ChessView extends View {
public ChessView(Context context, AttributeSet attrs, int defStyle) {
/* some code removed */
invalidateHandler = new InvalidateHandler(this);
new Thread(new Runnable() {
#Override
public void run() {
invalidateHandler.sendMessage(invalidateHandler.obtainMessage());
}
}).start();
}
/* some code removed */
private static class InvalidateHandler extends Handler {
public InvalidateHandler(ChessView view){
relatedChessView = new WeakReference<ChessView>(view);
}
#Override
public void handleMessage(Message msg) {
relatedChessView.get().invalidate();
}
private WeakReference<ChessView> relatedChessView;
};
private InvalidateHandler invalidateHandler;
}
As you can see :
I am creating a static inner class, subclass of the Handler class : as the android developpers guide recommands to avoid direct inner classes inside View subclasses
The Handler static inner class makes a call to the invalidate() method of my custom ChessView : so I decided to "wrap it" inside a WeakReference, as the android developper guide recommands to avoid hard references on View instances.
So here my questions :
Do I avoid memory leaks this way ?
Is WeakReference the best type, or should I use a SoftReference instead ?
And finally, will the custom view remains on the heap as long as the view is visible (or the related activity active) or may it be collected by the GC before, letting me with a null reference when calling relatedChessView.get() ?
Thanks in advance, and apologizes if my question is bad formulated.
Do I avoid memory leaks this way ?
Yes, but this isn't necessary.
Is WeakReference the best type, or should I use a SoftReference instead ?
WeakReference is the way to go in your case. SoftReference and WeakReference will be both available as long as there is a hard reference pointing to them. If there is no strong reference though, WeakReference will more likely to be collected while SoftReference will retain your object as long as there is no need to clean up memory (eg. SoftReference will stick around longer).
And finally, will the custom view remains on the heap as long as the view is visible (or the related activity active) or may it be collected by the GC before, letting me with a null reference when calling relatedChessView.get()?
Yes, it will. As i mentioned above WeakReference won't be collected while there are any Objects holding the contained Object's reference.
UPDATE: Fixed the information regarding Weak and Soft references based on #DeeV's answer to be more accurate.
I have a doubt about destruction of activities and objects.
While I attach & detach the activity from the AsyncTask I do not change the ArrayAdapter from the asynctask (see code). So, what I get is multiple activities being attached & detached (ought to orientation changes) and just one task running and modifying ONE adapter, which in turn is the one from the first activity that created the task. So, when I attach the task in the onCreate() I just set the adapter with the one which holds the task, which in turn has all the values processed (in the example just a dummie list of numbers).
How can this be possible? I thought that onDestroy() would erase the activity itself and its attributes, and therefore I would get a null pointer exception or something like that while trying to access the ArrayAdapter of the original activity from the AsynkTask, but the code below works!
private static class TestingTask extends AsyncTask<Void, Integer, Void> {
private TestingActivity mActivity; // extends ListActivity
private ArrayAdapter<String> mAdapter;
private boolean mIsFinished;
private TestingTask(Context activity) {
attach(activity);
mAdapter = (ArrayAdapter<String>)mActivity.getListAdapter();
mIsFinished = false;
}
private void attach(Context activity) {
mActivity = (TestingActivity)activity;
}
private void detach() {
mActivity = null;
}
protected Void doInBackground(Void... params) {
for (int i = 0; i < 100000; i++) {
publishProgress(i);
}
return null;
}
protected void onProgressUpdate(Integer... values) {
if (!isCancelled()) {
mAdapter.add(values[0].toString());
}
}
// ...
}
Is this because the task keeps an active reference to the ArrayAdapter object, and therefore it is not deleted? Or is it something else?
I also experienced another "similar case" in which I returned an Activity's attribute from onRetainNonConfigurationInstance() let's say A a, that had visibility over B b (which is another attribute of the Activity). Then, when trying to access b instance through a, there is no problem and I thought I would need a wrapper to hold the two instances (a and b), or else I would get an exception when trying to access b (which I do not actually save). I do not know if it is related width the previous case in which the objects that I supposed not to be available actually are there, maybe because of the active reference to them that causes no deletion?
Thank you!
I think I have found the answer to these questions and as I was wondering... it is related to the Garbage Collector and the use of strong references.
In Understanding weak references article it is said that:
if an object is reachable via a chain of strong references (strongly reachable), it is not eligible for garbage collection. As you don't want the garbage collector destroying objects you're working on, this is normally exactly what you want
In another article How Gargabe Collection works it is explained that:
if an object holds reference of another object and when you set container object's reference null, child or contained object automatically becomes eligible for garbage collection.
So, my conclusion is that:
In the first case: As I am setting activity to null in detach() there is no memory leak and all objects can be garbage collected unless the adapter, which has a strong reference. So, I understand that the activity and all other objects contained by it are deleted unless the adapter, this is what I actually want.
In the second case: As I am returning the container object (A a) in onRetainNonConfigurationInstance() and it has a strong reference to (B b), b instance is accessible too, because it can be reachable via a chain of strong references.
Hope this will be helpful. If anyone else wants to give his/her opinion it will be welcome!