Jason Ching Jason Ching - 5 months ago 25
Android Question

Why does my Activity leak?

I have an Activity that has an IntentFilter and a BroadcastReceiver, and I register them in my

onCreate()
.

IntentFilter filter = new IntentFilter(ACTION_RCV_MESSAGE);
filter.addCategory(Intent.CATEGORY_DEFAULT);
receiver = new MessageReceiver();
registerReceiver(receiver, filter);


But upon ending my app, Logcat says:


07-03 01:38:19.567: ERROR/ActivityThread(304): android.app.IntentReceiverLeaked: Activity com.intentservicetest5.IntentServiceTest5Activity has leaked IntentReceiver com.intentservicetest5.IntentServiceTest5Activity$MessageReceiver@44efe8a8 that was originally registered here. Are you missing a call to unregisterReceiver()?


But I do have

public void onDestroy(Bundle savedInstanceState){
unregisterReceiver(receiver);
}


Why is my Activity still leaking when I do call
unregisterReceiver()
?

Answer

Per the Activity Lifecycle, your app is killable any time after onPause() on most devices (any device with less than Android 3.0 / API level 12), and any time after onStop() on any device. That means onDestroy() is not guaranteed to be called. The best place in an Activity to register and unregister for broadcasts, then, is onResume() and onPause(), respectively:

@Override
protected void onResume() {
    super.onResume();

    IntentFilter filter = new IntentFilter(ACTION_RCV_MESSAGE);
    filter.addCategory(Intent.CATEGORY_DEFAULT);
    receiver = new MessageReceiver();
    registerReceiver(receiver, filter);
}

@Override
protected void onPause() {
    super.onPause();

    unregisterReceiver(receiver);
}

Note that as @VipalShah pointed out, you were using the wrong signature for onDestroy(). This is why unregisterReceiver() wasn't being called. Using @Override will help you catch this kind of problem.

Also note that the symmetry between onPause() and onResume() is intentional. If you look at the Activity lifecycle article linked to above, you'll see that onResume() is called during the initial start-up, not just when resuming from pause. So you only need to register for the broadcast here, not in onCreate().

Finally, note that I used your code for simplicity. In reality, you'll probably want to set up your IntentFilter and MessageReceiver in onCreate(), and then just call registerReceiver() in onResume().

Update: One more important note. As per Implementing the lifecycle callbacks:

Your implementation of these lifecycle methods must always call the superclass implementation before doing any work.

Many people here were showing the call to super.onWhatever() at the end of the method rather than at the beginning.