on android memory leak - android

In this offical blog site, I read below example of memory leak.
http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html
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);
}
To quote the original post "This code is very fast and also very wrong; it leaks the first activity created upon the first screen orientation change. When a Drawable is attached to a view, the view is set as a callback on the drawable. In the code snippet above, this means the drawable has a reference to the TextView which itself has a reference to the activity (the Context) which in turns has references to pretty much anything (depending on your code.)"
I do not understand this part. When the activity is recreated, the onCreate() method will be executed, the static Drawable object sBackground will be attached to the new TextView in the second activity. Meaning that the object sBackground will reference to the new textview instead of the old textview in the first activity, leaving the first activity un-referenced.
Can anybody tell me where is wrong in my reasoning? Thanks in advance~~
Oops, it seems this thread is a duplicate, someone asked exactly the same thing here
Understanding memory leaks in Android application
Sorry for my carelessness.

As far as I know after checking the source of View class, when a Drawable is attached to a view, the view is set as a callback on the drawable. Beside, the view also keep a reference to this Drawable. Please see this link http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.1.1_r1/android/view/View.java#16292.
And because static variable has long lifetime, then every time you recreate activity & set a Drawable to its view, the activity and its Context also keep a reference to the static Drawable. Then take memories as long as variable does.

Related

Android: reference to a dynamic View is no longer valid after screen rotation

Context:
i have n dynamically created ImageView(s) in an activity.
when an imageview is touched:
1) an IV object reference is stored vis the onTouch event.
2) a dialog opens; allowing the user to edit the image.
when the user has finished making edits, the changes are applied to the object which we have a stored reference.
however, if the user rotates the device while the Dialog is open, the activity is recreated; and the reference to the selected ImageView is no longer valid.
Issue:
So, the problem is that the recreated ImageView, produced from the rotation event, has a different reference to the reference that i have stored, meaning that i have no idea which ImageView to apply my edits to..
This seems like a common issue that could easily be solved, but i don't know how..
because the ImageView(s) are dynamically created, and there could be n quantity, i can't find view by ID.
i can't set a tag, as it is lost when the IV is recreated.
i read something about retainInstanceState, but this appears to be for fragments; not activities
Assign ids to your generated ImageViews. When dialog is shown you pass this id to dialog, when dialog finishes it returns your values and this id, then you just get ImageView by id, be it a new one, or old doesn't matter since the couple 'id - iv at position' are the same no matter how many times you recreate activities.
As example:
for (int i=0;i<20;i++) {
ImageView iv = new ImageView(context);
iv.setId(1337 + i); // Spice things up a little :D
addView(iv);
}
Then just pass required id iv.getId() to dialog, and when dialog finishes get iv by calling findViewById(id);
The easiest way to fix this is to add android:configChanges="orientation|<whatever other flags you need>" to your <activity> in AnroidManifest.xml, so the activity won't be recreated when orientation changes. However, using this method is officially discouraged:
Note: Using this attribute should be avoided and used only as a last resort
Another way would be to store in the dialog not the reference to ImageView, but its id instead.

About Romain Guy's Avoid memory leaks on Android

I am pretty newbie for android development and I just read Romain Guy's "Avoid memory leaks on Android" from the following link
http://www.curious-creature.org/2008/12/18/avoid-memory-leaks-on-android/
Then I did a little test by his famous code snippet on my android emulator
private static Drawable drawable;
private StringBuilder sb;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (drawable == null) {
drawable = getResources().getDrawable(R.drawable.ic_launcher);
}
sb = new StringBuilder();
for (int i = 0; i < 1024; i++) {
sb.append('a');
}
label.setCompoundDrawables(null, drawable, null, null);
setContentView(label);
}
This code is supposed to leak the first activity context while changing orientation. So I ran the program in emulator and change the orientation once (I also printed out 2 activity context ca.welcomelm.leaktest.MainActivity#45f81f98 and ca.welcomelm.leaktest.MainActivity#45f8d6f8
). Then I dump the HPROF and jump to list objects by incoming reference char[]. I though I am supposed to see 2 char[] with the pattern "aaaaaaaaaaaa...", which are referenced by those 2 activity context. But I swear I only saw one referenced by the second context. The leaked context is not there. I think it is GCed. Can anyone else confirm this? Thanks
Change:
if (drawable == null) {
drawable = getResources().getDrawable(R.drawable.ic_launcher);
}
To something like:
if (drawable == null)
drawable = getResources().getDrawable(R.drawable.ic_launcher);
else
Log.i("blablabla", "Drawable initialized in prior activity");
And then start doing the screen rotations. You'll notice that Log.i will kick-in because the drawable is marked as static and is initialized already in prior activity. This is the point where memory leaks start to begin.
Since modern devices are pretty much hefty with memory resources thus you do not notice such leaks so easily. However, if your application contains a lot of such references or running over a low-end device then you may get lucky to head memory-leaks soon enough.
You are correct, the original TextView and the objects it referenced (Context, Activity, and more) will be GC'ed once the drawable releases the reference to the TextView. The release occurs when label.setCompoundDrawables() (in #user3375547's example) or label.setBackgroundDrawable() (in Romain Guy's original example) is called.
However, the Context was leaked for a time because the Drawable kept a reference to the TextView too long, preventing the previous Context (and all other referenced objects) from being GC'ed when they could have been.
Now, imagine a situation where Android did not create another instance of the Activity. For example, the user backs out of Activity. Android may destroy the Activity at this point. In this case the static reference still exists and keeps the Context from being GC'ed, causing a leak. The GC will only happen when the Activity is recreated, the application process is terminated, or the Classloader is GC'ed as #uDevel mentions in the comments below.

Android: setImgVisibility() error

In my app I have a header with icon hidden, I have a adapter with a listview when I click the listview I go to a login screen using listener, when the login is successful is should come back to listview(adapter) and icon should get visible on header.
In the login activity I have the following code:
public void onClick(View v) {
String password = etPassword.getText().toString();
if(password.equals("guest")){
SearchAdapter.setImgVisibility();
} else {
//-----
}
finish();
}
In my adapter I am calling the setImgVisibility() as follows, but it is not working
public static void setImgVisibility() {
img.setVisibility(View.VISIBLE);
}
I am getting a Nullpointerexception near the line img.setVisibility(View.VISIBLE);
I am stuck here and don't know what I am doing wrong. Any suggestions or help is appreciated
I would imagine that img is null. You need to look at where this value is set and make sure happens before you call the method setImgVisibility.
Show more of your complete code for people to help further.
Additionally, i've just noticed you've used a static reference to your search adapter, you should be really careful using statics, especially where any referencing of images is concerned as images can be bound to the context, as such unless you nullify the static you will end up with a memory leak. (this used to be an old problem, not sure its still valid, but i would still avoid using a static reference).
Without more code we're not likely to be able to properly help you. For example are you switching activities when logging in? If you are, this won't work at all.
[given the comment below] If you switch activities then your activity containing the list view is going to be destroyed and then rebuilt then you navigate back to it. or it will at least go through the activity lifecycle. This means you can set the icon during the instantiation of the header img.
You could store your logged in state as a property of the Application or a preference. Grab this value when you set the header image and set the image accordingly.
your img object is null. Is your img object is same as View v then you can pass v in setImgVisibility() and then set v.setVisibility(View.VISIBLE)

Why does drawable have a reference to a view?

a very old link about memory leaks talks about a drawable that has a reference to a view (link here) .
i have some simple questions regarding it:
why does a drawable have a reference to a view ?
what does the drawable do to the view ?
does it have a reference to all of the views that use it?
do all kinds of drawable have references to views ?
I was reading Romain Guy's article too. The website/ blog is now gone, Wayback link.
Drawable's have a private field (mCallback) which refers to an instance of a class which implements the Drawable.Callback interface, documented here. View implements this interface, and this callback reference is automatically set by the system when view.setBackground is called.
public class View extends Object implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {}
Why set this field? It's usage is an implementation detail of Drawable, so it's difficult to know what exactly its usage is. "The drawable uses this interface to schedule/ execute animation changes." is all I could get from the docs. I guess the main reason is to call void scheduleDrawable(Drawable, Runnable, TimeToExecuteMeasuredSinceAppLaunch) doc.
Implement this interface if you want to create an animated drawable that extends Drawable. Upon retrieving a drawable, use Drawable#setCallback(android.graphics.drawable.Drawable.Callback) to supply your implementation of the interface to the drawable; it uses this interface to schedule and execute animation changes.
So to answer your questions specifically
why does a drawable have a reference to a view ? To call the interface (scheduleDrawable and other interface methods) to animate itself. And also, "to schedule and execute animation changes."
what does the drawable do to the view ? The drawable calls those methods in the interface.
does it have a reference to all of the views that use it? The Drawable has 1 callback (so only 1 view can be used), and it can set it with setCallback documented here.
do all kinds of drawable have references to views ? If you set the setCallback, yes. It doesn't have to be "animated" to have the reference, because this (setCallback) is automatically done with View.setBackground(Drawable) and ImageView. according to the Drawable docs.
Finally, I find his post confusing, as he glossed over this detail which was the fundamental cause of the memory leak (the Drawable.Callback interface and more importantly, the mCallback field). In the end, the callback in Drawable is stored as private WeakReference<Callback> mCallback = null;. It is a weak reference which should not cause the memory leak he says. Maybe this was a change to Android after his blog post.
EDIT: Aha! It was Romain who then changed it in 2010:
1.why does a drawable have a reference to a view ?
A drawable have a reference to a View due to allow it the intercept the view state , suppose that you have a selector-drawable that when view mode change in example pressed, focus, disable change it background

Android - memory leak or?

two days ago i noticed something. I have a spinner over a map activity. In the OnCreate() method of the activity i populate the spinner with data. After that i start the heap analyzer in DDMS i begin to open/close the spinner. I noticed the VM allocate memory when i open the spinner items, but when i close it, the VM do no free this memory. I've tried to start the GC, but the memory is still allocated. i did this 20 times one by one and the allocated memory increased from 3.5MB to 7MB. What is wrong? I found an issue in google groups, but they haven't answered yet.
Spinner memory leak
I rewrite all my code in the spinner adapter, but the issue still remains.
I read some advices in this topic
Avoid memory leaks
There is something i did not get:
When a Drawable is attached to a view, the view is set as a callback on the drawable. In the code snippet above, this means the drawable has a reference to the TextView which itself has a reference to the activity (the Context) which in turns has references to pretty much anything (depending on your code.)
What does it mean? If i have a textview and set it a drawable object (i noticed the drawable is static), the textview object has a reference to the drawable object and the drawable object has a reference to the view too? If this is true, they become undestroyable by the GC because they both have references to each other? What is this back-reference (callbacks) dependencе between the objects?
Sorry I can't help you on your Spinner problem but I can have a try on the second part:
Romain Guy post on android developer blog explain two important things.
First:
When you create a View (TextView, ImageView...) you must not create it with the activity Context
// DO NOT DO THIS
TextView label = new TextView(this);
Otherwise the View get a reference to your activity and will never be deallocated.
Instead, when you create a View programatically, you have to use the application context:
TextView label = new TextView(getApplicationContext());
Second:
When you link a Drawable to an View, it keeps a callback on your activity via the Context. If you leave it, it will leak memory when your activity is destroy.
The thing to do to avoid that is to "set stored drawables' callbacks to null when the activity is destroyed" so for example whith an ImageView:
protected void onDestroy() {
imageView.getDrawable().setCallback(null);
super.onDestroy();
}
You have to do the same for the background drawable...
Hope it helps.

Categories

Resources