I pass a handler created on mainUI thread from Activity
and passed to a thread which performs some network operation and when i obtain result i send the result back to the activity using the handler.
This approach had issue in memory leaks when i went through these links:
Inner ClassHandler Memory Leak
Android Developers
So i had implemented WeakReference
, and kept the activity instance using WeakReference
. But i am still seeing Activity
instance alive even after activity is destroyed.
I created a Handler
inside activity and passed activity instance as weakreference to handler.
By the time my Handler
responds with a message delivered to it after 10secs, Activity
is destroyed. But the weak reference still has the Activity
instance and i am seeing the Toast
, after Activity
is destroyed.
Is there some where my understanding wrong ?
Can someone explain how to handle messages delivered to a handler,but the UI is not around ?
import java.lang.ref.WeakReference;
import android.os.Handler;
import android.os.Message;
public abstract class SingleParamHandler <T> extends Handler
{
private WeakReference<T> mActivityReference;
public SingleParamHandler(T activity) {
mActivityReference = new WeakReference<T>(activity);
}
@Override
public void handleMessage(Message msg) {
if (mActivityReference.get() == null) {
return;
}
handleMessage(mActivityReference.get(), msg);
}
protected abstract void handleMessage(T activity, Message msg);
}
import android.app.Activity;
import android.os.Bundle;
import android.os.Message;
import android.widget.Toast;
public class MainActivity extends Activity {
MyHandler<MainActivity> handler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main1);
handler = new MyHandler<MainActivity>(this);
new Thread(new MyRunnable(handler)).start();
}
public void onDestroy() {
super.onDestroy();
System.out.println("######## Activity onDestroy() ###### ");
}
private class MyRunnable implements Runnable {
private Handler mHandler;
public MyRunnable(Handler handler) {
mHandler = handler;
}
public void run() {
try {
Thread.sleep(10000);
mHandler.sendMessage(Message.obtain(handler, 1));
} catch ( Exception e) {
e.printStackTrace();
}
}
}
private static class MyHandler<T> extends SingleParamHandler<T> {
public MyHandler(T activity) {
super(activity);
}
@Override
public void handleMessage(T act, Message msg) {
if(msg.what == 1) {
Toast.makeText((MainActivity)act, "Called after activity destroyed", Toast.LENGTH_LONG).show();;
}
}
}
}
Based on the response obtained, i am updating the answer here. You may do it in the way u liked. But this is one way.
Added the below function in SingleParamHandler
public void clear() {
mActivityReference.clear();
}
And in Activity onDestroy()
public void onDestroy() {
super.onDestroy();
System.out.println("######## Activity onDestroy() ###### ");
handler.clear();
}
You don't need a WeakReference
here. The Handler
can just contain a reference to the Activity
. In activity's onDestroy()
just call a method on MyHandler
that sets the reference to the Activity
to null
. Check for null
in handleMessage()
.
Another choice would be this: in activity's onDestroy()
call a method that interrupts the sleeping thread so that it shuts down before sending the message.
There's no guarantee that Android will really delete an object from memory if it's not required to do so. In other words, Activity objects can stay in memory even after onDestroy()
has been called (if there's enough memory available). On the other hand, there's no guarantee that onDestroy()
will be called if there's not enough memory; quite to the contrary, Android is allowed to kill your whole process after calling onPause()
on your current Activity (depending on the Android version).
I think there's a better path to follow for your purpose. What you may want to do is attach, detach and possibly re-attach (e.g. on configuration changes) Activities to your Service. Don't hope for the garbage collector to do the work for you. Rather, make it explicitly.
Subclass Activity
and override the lifecycle methods as well as startActivity()
and startActivityForResult()
to let your Service know who's in charge right now. Of course, that's only a best-effort approach since some callbacks aren't guaranteed, but that only matters in certain situations which aren't dangerous. For example, your Activity won't detach from your Service in onPause()
, but it could get killed right afterwards. But either your Service runs in the same process, so it gets killed at the same time. Or it runs in a different process, but then Android will notice the broken connection and may or may not kill the service as well; if not, then all you need to do is implement it in a robust fashion to be able to deal with the connection loss.
Update
After reading your comment: You're right, I didn't address that specifically.
i am figuring out how to avoid messages being sent to a handler which is created in a activity which is destroyed
Given your code above, and assuming that you really just want to display Toast
s with an Activity as long as it exists, the following approach should help.
Thread
is supposed to serve more than one Activity, extend it such that Activities can register with the Thread after it is created. If your Thread
just serves one Activity
, pass the Activity
reference along with the Handler
reference upon your Thread
's (Runnable
's) construction.Thread
sends the message via the Handler
, check activity.isDestroyed()
. If the Activity is not destroyed, send the message. If the Activity is destroyed, do not send the message.Runnable
's run()
method or set it's Activity
reference to null
if it finds that the Activity
has been destroyed.This should fix your above code. However, if your scenario grows, other approaches may be more suitable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With