I'm getting a memory leak on Android reported by LeakCanary but I can't find out how to trace it.
I have an activity called Splash which calls one Service to fetch configuration data, then via a handler pings back to the activity.
//Started like this in the Splash activity
ConfigDumpService.start(this, this, BaseService.DATA_CALLBACK_ACTION_SIMPLE);
where the start method is:
public static void start(final Context context, final Handler.Callback handlerCallback, final int callbackAction) {
final Messenger messenger = new Messenger(new Handler(handlerCallback));
final Intent intent = new Intent(context, ConfigDumpService.class);
intent.putExtra(BaseService.PARAM_MESSENGER, messenger);
intent.putExtra(BaseService.PARAM_CALLBACK_ACTION, callbackAction);
context.startService(intent);
}
The Splash activity implements Handler.Callback
@Override
public boolean handleMessage(final Message msg) {
L.p("In Splash handleMessage(), thread: " + Thread.currentThread().getName());
if (BaseService.DATA_RETRIEVE_SUCCESS == msg.arg1) {
L.p("Message from ConfigService service is SUCCESS!");
startApp();
} else {
L.p("Message from ConfigService service is FAIL!");
showCannotContinueDialog();
}
return true;
}
The ConfigDumpService
// Previously fetched some data...
final Message message = Message.obtain();
message.setData(bundle);
if (successful) {
message.arg1 = BaseService.DATA_RETRIEVE_SUCCESS;
} else {
message.arg1 = BaseService.DATA_RETRIEVE_FAIL;
}
try {
final Messenger messenger = startIntent.getParcelableExtra(BaseService.PARAM_MESSENGER);
messenger.send(message);
} catch (RemoteException e) {
L.p("In onHandleIntent RemoteException");
e.printStackTrace();
}
stopSelf();
Another place where a handler is created in the Splash activity is to start the main activity after a small delay:
final Handler handler = new Handler();
final Runnable mRunnable = new Runnable() {
public void run() {
DialogManager.removeAllDialogs();
// Let the base activity know we're just starting the app
BaseActivity.startWithHome(Splash.this, homeId, false, true);
Splash.this.finish();
}
};
handler.postDelayed(mRunnable, splashImageLoadWasSuccessful ? mSplashScreenWaitMilliseconds : 0);
The call stack isn't super helpful. I'd appreciate any tips.
Thanks
I think it's related to how you instantiate your Handler
. Since you are implementing the Handler.Callback
in your SplashActivity
your are keeping a hard reference in your message queue. Instead You should implement your own Handler
class and if it is an inner class it should be static
. And within this custom Handler
you should have a WeakReference
to the Activity
you pass in. This post explains the problem in more detail and shows you how to solve this issue. Hope this helps ;)