Search code examples
androidleakcanary

Track memory leak reported by LeakCanary on Android


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

enter image description here


Solution

  • 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 ;)