Do I need to unregister 'anonymous' BroadcastReceiver

2.4k Views Asked by At

I recently asked a question about checking on the state of a sent SMS and the answer given was a code snippet which registered two 'anonymous inner' (please correct my terminology if it is incorrect) BroadcastReceivers to listen for SMS sent/delivered broadcasts. These receivers were only required to receive data regarding the SMS my application had just sent, so were not required to listen permanently.

My immediate thought was "well, I'll need to unregister them after I've finished with them", but is this correct? I asked the poster this as he hadn't included any unregister code, but got no reply. The code seems to be a pretty standard way of doing what I want as it appears on numerous Android dev sites. Here it is:

//---sends an SMS message to another device---
private void sendSMS(String phoneNumber, String message)
{        
    String SENT = "SMS_SENT";
    String DELIVERED = "SMS_DELIVERED";

    PendingIntent sentPI = PendingIntent.getBroadcast(this, 0,
        new Intent(SENT), 0);

    PendingIntent deliveredPI = PendingIntent.getBroadcast(this, 0,
        new Intent(DELIVERED), 0);

    //---when the SMS has been sent---
    registerReceiver(new BroadcastReceiver(){
        @Override
        public void onReceive(Context arg0, Intent arg1) {
            switch (getResultCode())
            {
                case Activity.RESULT_OK:
                    Toast.makeText(getBaseContext(), "SMS sent", 
                            Toast.LENGTH_SHORT).show();
                    break;
                case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
                    Toast.makeText(getBaseContext(), "Generic failure", 
                            Toast.LENGTH_SHORT).show();
                    break;
                case SmsManager.RESULT_ERROR_NO_SERVICE:
                    Toast.makeText(getBaseContext(), "No service", 
                            Toast.LENGTH_SHORT).show();
                    break;
                case SmsManager.RESULT_ERROR_NULL_PDU:
                    Toast.makeText(getBaseContext(), "Null PDU", 
                            Toast.LENGTH_SHORT).show();
                    break;
                case SmsManager.RESULT_ERROR_RADIO_OFF:
                    Toast.makeText(getBaseContext(), "Radio off", 
                            Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }, new IntentFilter(SENT));

    //---when the SMS has been delivered---
    registerReceiver(new BroadcastReceiver(){
        @Override
        public void onReceive(Context arg0, Intent arg1) {
            switch (getResultCode())
            {
                case Activity.RESULT_OK:
                    Toast.makeText(getBaseContext(), "SMS delivered", 
                            Toast.LENGTH_SHORT).show();
                    break;
                case Activity.RESULT_CANCELED:
                    Toast.makeText(getBaseContext(), "SMS not delivered", 
                            Toast.LENGTH_SHORT).show();
                    break;                        
            }
        }
    }, new IntentFilter(DELIVERED));        

    SmsManager sms = SmsManager.getDefault();
    sms.sendTextMessage(phoneNumber, null, message, sentPI, deliveredPI);

The code works fine.

What's more, it does not get notified of any SMS sent/delivered events which occur external to my app. E.g. I can send an SMS after these BroadcastReceivers have been registered and not see any Toast messages.

So, I have two questions:

  1. do I need to unregister these BroadcastReceivers?
  2. If not, why not?
4

There are 4 best solutions below

6
On

You will need to unregister. Else, it will most probably crash. I am quite sure about it. But if you say that it's not crashing, then, well, need to look into it :)

Because, Android will think you forgot to unregister. It expects you to unregister. "Note: If registering a receiver in your Activity.onResume() implementation, you should unregister it in Activity.onPause(). (You won't receive intents when paused, and this will cut down on unnecessary system overhead). Do not unregister in Activity.onSaveInstanceState(), because this won't be called if the user moves back in the history stack." <===== From the docs

1
On

Simply save your BroadcastReceiver to an instance so you are able to unregister it ;-)

Just change this line from:

registerReceiver(new BroadcastReceiver(){

to:

BroadcastReceiver smsReceiver=new BroadcastReceiver(){...}
registerReceiver(smsReceiver);

Later you can execute than:

unregisterReceiver(smsReceiver);

Just remember to save the smsReceiver as a class member.

3
On

So if I understand what you've written correctly, it looks like the BroadcastReceiver should only exist within the sendSMS() call, so even though they are registered for the SENT or DELIVERED Intents, they may not be around to receive them. Also, I believe you always want to unregister receivers when you are done with them, I've seen the debugger warn about leaking receivers.

Would it work better if you had class parameters for like sent_broadcast_receiver and delivered_broadcast_receiver that contain a reference to the receivers you are registering, that way the objects persist, and can be unregistered after they are done.

EDIT: Whatever object holds the BroadcastReceivers for SENT and DELIVERED should persist until the intents return, or the Intents (since they are PendingIntents) should direct to something that can be woken up, if that's what you desire.

0
On

What's more, it does not get notified of any SMS sent/delivered events which occur external to my app.

You are dynamically registering receivers. If you want to listen to intents from other applications, you need to register your receiver in the manifest file. That way it will be always active. And you won't need to unregister it.