Android Handler, memory leak? - android

I understand that the Handler may leak memory in Android, but I am not very sure what I did is correct:
public class MyActivity extends Activity
{
Handler handler;
HashMap<String, String> bigData = new HashMap<String, String>();
protected void onCreate(undle savedInstanceState) {
super.onCreate(savedInstanceState);
handler = new WeakHandler(){
public void handlerMessage(Message msg, Context conetxt)
{
// i think this usage is wrong, still leak, am I right?
Log.v("MyActivity", bigData.get("abc"))
}
}
};
static class WeakHandler extends Handler {
private final WeakReference<Context> ref;
public WeakHandler(Context t) {
super();
ref = new WeakReference<Context>(t);
}
#Override
public final void handleMessage(Message msg) {
final Context context = ref.get();
if (context != null) {
handleMessage(msg, context);
}
}
public void handlerMessage(Message msg, Context conetxt)
{
}
}
Also, If I just want to use a Handler to "post", "postDelayed", "removeCallbacks", I do not care about handleMessage, is that safe to just create a new Handler instance to do that, like this:
new Handler().postDelayed(some Runnable);

When you instantiate the WeakHandler, it won't be an actual WeakHandler class, but an Anonymous class which extends WeakHandler class. An Anonymous class is never static so it will have an implicit reference to the containing class (namely the MyActivity), besides the explicit WeakReference.
Since the onCreate() runs on the main thread, the Handler - that is created inside the onCreate() - will be associated with the main tread (actually the main Looper that is created when the Application starts). If you post a Message (or a Runnable, since the Runnable will be wrapped around with a Message) to the Handler, that Message/Runnable will have a reference to the Handler.
So given you postDelayed something with 5 mins delay. After destroying the Activity (eg. because of rotation), the main Looper will have a reference to the Message/Runnable, the Message/Runnable will have a reference to the Handler, Handler will have a reference to the Activity for 5 mins. This is a leak.
And yes, the "Log.v("MyActivity", bigData.get("abc"))" line leaks the activity as well.

Use MAT Analyzer to check for memory leaks. http://www.youtube.com/watch?v=_CruQY55HOk.
The talk is about memory leak.
My guess the reference to the context is causing leaks. Use getApplicationContext();
This context will live as long as your application is alive.
http://android-developers.blogspot.in/2009/01/avoiding-memory-leaks.html.

Related

Can this code avoid the Android handler memory leak?

handler1 is a leak.
I want to convert handler1 code to handler2 code. Is that OK?
What is the difference between the two codes?
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// leaks!
Handler handler1 = new Handler()
{
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("LOG", "Hello~1");
}
};
Handler handler2 = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
Log.e("LOG", "Hello~2");
return false;
}
});
handler1.postDelayed(new Runnable() {
#Override
public void run() { }
}, 60000);
handler2.postDelayed(new Runnable() {
#Override
public void run() { }
}, 60000);
finish();
}
}
Why Leak Warning for handler1?
For the reason on leak warning, this article explains very well.
Quoting from the article
In Java, non-static inner and anonymous classes hold an implicit reference to their outer class. Static inner classes, on the other hand, do not.
So when you created handler1 by anonymous class, it will holds a reference to the MainActivity instance and MainActiviy can not be garbage collected.
Solution
Quoting from the article again
To fix the problem, subclass the Handler in a new file or use a static inner class instead. Static inner classes do not hold an implicit reference to their outer class, so the activity will not be leaked. If you need to invoke the outer activity’s methods from within the Handler, have the Handler hold a WeakReference to the activity so you don’t accidentally leak a context. To fix the memory leak that occurs when we instantiate the anonymous Runnable class, we make the variable a static field of the class (since static instances of anonymous classes do not hold an implicit reference to their outer class):
Following the article, update your code as follows:
public class MainActivity extends AppCompatActivity {
private static class MyHandler extends Handler {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.e("LOG", "Hello~1");
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Handler handler1 = new MyHandler();
handler1.postDelayed(new Runnable() {
#Override
public void run() { }
}, 60000);
finish();
}
}
Can handler2 solve the problem?
The answer from #Michael This Handler class should be static or leaks might occur: IncomingHandler
provides the solution.
Quoting from #Michael answer
As I understand it, this will not avoid the potential memory leak. Message objects hold a reference to the mIncomingHandler object which holds a reference the Handler.Callback object which holds a reference to the Service object. As long as there are messages in the Looper message queue, the Service will not be GC. However, it won't be a serious issue unless you have long delay messages in the message queue.
In your case, handler2 will hold a reference to Handler.Callback object.And since Handler.Callback is created by anonymous class, hence it will hold a reference to MainActiviy instance too. So MainActiviy instance can not be garbage collected also.
I try this code on emulator(android 28) and dump the memory, the profiler shows nothing to leak, I also try Leakcanary and shows same result. This makes me doubt the accuracy of the articles on the web. Then I noticed the difference,if I use Kotlin write this logic, the memory will not leak, but use the Java code will leak.
Later I found something interesting. In java, whether use outer class method or not,the anonymous inner class will hold outer object reference by constructor. In kotlin,if inner logic is not use outer class method,the anonymous inner class dose not hold the outer object reference.
I used Handler in Kotlin code that catches the incoming Bluetooth incoming message. This code has no lint leaks warnings:
private val incomingMsgHandler: Handler = Handler { msg ->
msg.obj?.let {
if (it is ByteArray) {
val msgStr = String(it)
setIncomingMessage(msgStr)
}
}
true
}
In short description, this uses Handler constructor with lambda callback.

This Handler class should be static or leaks may occur (null)

This Handler class should be static or leaks may occur (null)
Is the 'class' this message referring to 'MyActivity' here, since Handler is an object and i did declare it static. Should I ignore it or there something I should add, like 'static' somewhere in the 'MyActivity' declaration (I tried this and got errors). I notice 'WeakReference' is often suggested for this lint warning.
public class MyActivity extends Activity{
...
static Handler handler;
...
handler = new Handler()
{
public void handleMessage(Message msg) {
since Handler is an object and i did declare it static
You declared the data member to be static. However, you are using anonymous inner class, and therefore your subclass of Handler is not static.
Instead of:
handler = new Handler() {
public void handleMessage(Message msg) {
// do cool stuff
}
};
use:
handler=new MyVeryOwnHandler();
where MyVeryOwnHandler is either a regular Java class or a static inner class:
private static class MyVeryOwnHandler extends Handler {
public void handleMessage(Message msg) {
// do cool stuff
}
};
Note that the error is that the class needs to be static; it does not say that the object needs to be static.
To avoid leaks I also migrated to a static nested class. The explanation from Android Studio says this, it might help:
Since this Handler is declared as an inner class, it may prevent the
outer class from being garbage collected. If the Handler is using a
Looper or MessageQueue for a thread other than the main thread, then
there is no issue. If the Handler is using the Looper or MessageQueue
of the main thread, you need to fix your Handler declaration, as
follows: Declare the Handler as a static class; In the outer class,
instantiate a WeakReference to the outer class and pass this object to
your Handler when you instantiate the Handler; Make all references to
members of the outer class using the WeakReference object.
Before migration I used the implementation of a Looper thread as suggested by developer.android.com , which in the end lead to the warning. *scratch
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}

Will handler inside a method leak memory?

I know handler declared in a class may leak memory since it holds a reference to its outer class. In this case, we should use static nested class with weak reference.
But What if a handler is declared inside a method. I faced below case and not sure is it a correct implementation. Could someone please explain or give me a hint? I even don't know what I should search for.
private void methodA(){
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
methodB();
}
}, 10*1000);
private void methodB(){
//textView holds a reference to a activity
textView.setText("hello");
}
It can under certain conditions. If the runnable passed is an anonymous or inner class, as in your example, it holds an implicit reference to 'this' and prevents 'this' from being garbage collected until the runnable is processed off the queue (so if your method never runs, like if your handler thread gets stopped without clearing the queue, it will leak).
In the case where you are worried about the conditions for a memory leak occurring or hanging onto objects too long, then you need to make your runnable a static class that has a weak reference initialized in the constructor, something like:
private static MyRunnable implements Runnable
{
private final WeakReference<MyClass> myClass_weakRef;
public MyRunnable(MyClass myClassInstance)
{
myClass_weakRef = new WeakReference(myClassInstance);
}
#Override
public void run()
{
MyClass myClass = myClass_weakRef.get();
if(myClass != null)
myClass.methodB();
}
}
private void MethodA()
{
Handler handler = new Handler();
handler.postDelayed(new MyRunnable(this), 10*1000);
}
Creating a Handler inside your method isn't a special case. It falls under the same circumstances, in that the Message you post will live in the message queue until it's processed.

What is the difference between Handler implementation with WeakReference and without it?

I making Service with Handler and I need Handler to use some Service methods. As Handler must be static, I can access Service methods without Service reference in Handler.
So I did this way:
private static class ServiceHandler extends Handler {
MyService service;
public ServiceHandler(MyService service) {
this.service = service;
}
#Override
public void handleMessage(Message msg) {
...
}
}
But also found that this is the right way to do the job:
private static class ServiceHandler extends Handler {
private final WeakReference<MyService> mMyService;
public ServiceHandler(MyService service) {
mMyService = new WeakReference<MyService>(service);
}
#Override
public void handleMessage(Message msg) {
MyService service = mMyService.get();
...
}
}
What is the difference in this two ways to use Service in Handler?
The first code example sets a member variable to a reference of the calling Service (which I assume is the outer class of this inner class). This is exactly the same as if you had removed the static keyword for the inner class, because now the inner class is holding a reference to an instance of the Service class (which is what you are usually trying to avoid when you use the static keyword).
The second code example uses a weak reference, which means that the garbage collector can clean up (destroy) the instance of the Service class, even though you are holding a reference to it in your Handler. If that occurs, the call to mMyService.get() will return null, so you had better check for that in your code.
In practice, there are no differences between these 2 code examples since you won't be using the Handler once the Service has been destroyed.

Will this code from Android SDK sample cause a memory leak of the parent Activity?

So from reading/research about memory leaks it suggests to make all inner classes static to avoid memory leaks. However, by looking at the SDK samples (specifically TicTacToeLib) they implement their callbacks without the use of static inner classes. Will this cause a memory leak? If not, why?
private Handler mHandler = new Handler(new MyHandlerCallback());
private class MyHandlerCallback implements Callback {
public boolean handleMessage(Message msg) {
if (msg.what == MSG_COMPUTER_TURN) {
// Pick a non-used cell at random. That's about all the AI you need for this game.
State[] data = mGameView.getData();
int used = 0;
while (used != 0x1F) {
int index = mRnd.nextInt(9);
if (((used >> index) & 1) == 0) {
used |= 1 << index;
if (data[index] == State.EMPTY) {
mGameView.setCell(index, mGameView.getCurrentPlayer());
break;
}
}
}
finishTurn();
return true;
}
return false;
}
}
Yes, this sample will cause a leak in case it keeps a Message in the queue. But it's not a very severe leak since it is usually limited to a rather short amount of time.
But there is a rather simple way to prevent the leak:
Put the following two classes into your project
/** Callback that decouples the wrapped Callback via WeakReference */
public class SafeCallback implements Handler.Callback {
private final WeakReference<Handler.Callback> mCallback;
public SafeCallback(Handler.Callback callback) {
mCallback = new WeakReference<Handler.Callback>(callback);
}
#Override
public boolean handleMessage(Message msg) {
Handler.Callback callback = mCallback.get();
if (callback != null)
return callback.handleMessage(msg);
// else warn, return true, ..?
return false;
}
}
/** replacement for anonymous inner Handler implementations */
public abstract class SafeHandler implements Handler.Callback {
#Override
public abstract boolean handleMessage(Message msg);
public final Handler get() {
return new Handler(new SafeCallback(this));
}
public final Handler get(Looper looper) {
return new Handler(looper, new SafeCallback(this));
}
}
And now you can use Handler / Callback almost as you used to do but it's no longer leaking.
So either like
public class TestActivity extends Activity {
private Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new SafeHandler() { // << instead of new Handler() {
#Override
public boolean handleMessage(Message msg) {
// handle message
return false;
}
}.get(); // << Notice this added .get()
}
}
or like
public class TestActivity2 extends Activity implements Handler.Callback {
private Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler(new SafeCallback(this)); // << wrapped in SafeCallback
}
#Override
public boolean handleMessage(Message msg) {
// handle message
return false;
}
}
The leak problem with Handler is that each Message / Runnable (which is actually wrapped in a Message) knows it's target, i.e. has a hard reference to the Handler or Callback. And if that target is a non-static inner class, it will have an implicit hard reference to the outer class which is typically an Activity.
That means that as long as there are Messages enqueued for your Handler, your whole Activity can't be garbage collected.
To solve this issue that chain of hard references from Message to Activity has to be broken. The SafeCallback class does exactly that by keeping just a WeakReference towards your Activity.
That means, the Message has now a hard reference to SafeCallback but the part bind there can now be garbage collected. In case that happens Handler.Callback callback = mCallback.get(); will turn out null and the Message is simply discarded. There is no more useful target anyways. It is still leaking the SafeCallback itself but that's a pretty much empty class so it won't lead to problems.
I would approach it from the standpoint of what use case are you trying to solve, and not what the language itself is doing. If you "nested class" (not inner class because inner classes can't be static) needs to be able to call non-static methods on its parent class, or read non-static members, then you don't have much choice but to make it non-static. If you can get away with not accessing any of the parent class's non-static resources, then by all means do so (you'll save some memory that way anyways). However, if you're worried about memory leaks and you're going to make the nested class private as in your example, then you really shouldn't have anything to worry about because instances of that class can only be created locally to the parent class (unless you create a static member of the parent class that holds a reference to an instance of the nested class, in which case that object would be around until the parent class gets unloaded by the VM).
In summary, I wouldn't personally worry too much about whether or not your nested class is declared as static or non-static, but focus more on the lifecycle of instances of that class, if you're worried about leaking memory.

Categories

Resources