0

I wrote a simple Broadcast Receiver which catches incoming calls and starts activity with the caller's number:

package com.example.nrsearch;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;

public class CallReceiver extends BroadcastReceiver {
public CallReceiver() {
}

public Context context;

@Override
public void onReceive(Context context, Intent intent) {
    Log.i("CallReceiverBroadcast", "onReceive() is called. ");
    this.context = context;
    TelephonyManager teleMgr = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
    PhoneStateListener psl = new PhoneStateListener() {
        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            Log.i("CallReceiverBroadcast", "onCallStateChanged() is called. ");
            switch (state) {
            case TelephonyManager.CALL_STATE_RINGING:
                Log.i("CallReceiverBroadcast", "Incoming call caught. Caller's number is " + incomingNumber + ".");
                startNumberDisplayActivity(incomingNumber);
            }
        }
    };
    teleMgr.listen(psl, PhoneStateListener.LISTEN_CALL_STATE);
    teleMgr.listen(psl, PhoneStateListener.LISTEN_NONE);
}

public void startNumberDisplayActivity(String incomingNumber) {
    Intent i = new Intent(context, MainActivity.class);
    i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    i.putExtra("incomingNumber", incomingNumber);
    context.startActivity(i);
}
}

However, after the first incoming call I feel like my device's battery starts to drain pretty quickly. I'm afraid that some process still prevents my Broadcast Receiver from disconnecting (I mean my Broadcast Receiver may be running forever). So, is there something in my code that could cause such a behavior and does this line really stops the TelephonyManager from listening for call state changes: teleMgr.listen(psl, PhoneStateListener.LISTEN_NONE); or should I do it some other way?

EDIT: I'm almost sure that this class causes battery drain, because now I'm testing battery life with my app uninstalled and it's much lower than previous when this app was installed and broadcast receiver was called. I can't swear that this class is the cause of the drain but the battery consumption difference is clearly visible with and without this app. Could somebody look at this code and say what could cause the battety drain? Thanks in advance!

Salivan
  • 1,876
  • 7
  • 26
  • 45

1 Answers1

1

in the method

@Override 
public void onPause() {

super.onPause();
mActivity.unregisterReceiver(myReceiver);

}

You can put this other places, but that's a good one. DO your registration in onResume.

Also please read the broadcastreceiver docs, they don't work the way you seem to believe they do:

http://developer.android.com/reference/android/content/BroadcastReceiver.html

Basically the receiver lifecycle is:

Receiver Lifecycle

A BroadcastReceiver object is only valid for the duration of the call to onReceive(Context, Intent). Once your code returns from this function, the system considers the object to be finished and no longer active.

This has important repercussions to what you can do in an onReceive(Context, Intent) implementation: anything that requires asynchronous operation is not available, because you will need to return from the function to handle the asynchronous operation, but at that point the BroadcastReceiver is no longer active and thus the system is free to kill its process before the asynchronous operation completes.

In particular, you may not show a dialog or bind to a service from within a BroadcastReceiver. For the former, you should instead use the NotificationManager API. For the latter, you can use Context.startService() to send a command to the service.

Edit:

As per your comments - the concern about the BroadcastReceiver eating battery life isn't real. The receiver only lasts as long as it takes to run the code in it's on receiver method. At that point Android will clean it up as it deems nescesary. If anything in your code is breaking this it would be:

this.context = context;

/// these three lines
    TelephonyManager teleMgr = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
   .....
    teleMgr.listen(psl, PhoneStateListener.LISTEN_CALL_STATE);
teleMgr.listen(psl, PhoneStateListener.LISTEN_NONE);

since you create a object and then try to listen on it.

However you should really read the docs:

anything that requires asynchronous operation is not available,

Which is EXACTLY what you are doing in your code - attaching to a service and and waiting for its asyncronous response. This isn't acceptable in a BroadcastReceiver, and is clearly indicated in the docs at this point:

In particular, you may not show a dialog or bind to a service from within a BroadcastReceiver.

Nathaniel D. Waggoner
  • 2,856
  • 2
  • 19
  • 41
  • I'm not sure this is what I need. Shouldn't the Broadcast Receiver disconnect itself after finishing its work? And I don't really want to interact with it from the activity because my this Broadcast Receiver runs in background and the activity is started only when an incoming call is caught. – Salivan Mar 21 '14 at 15:28
  • When and how are you registering your receiver? Did you read the lifecycle docs? You're talking about broadcast receivers as if they were Async Tasks or Activities - they aren't. They're one offs, that respond to intents and which can be used to start other things. You register your receiver, but it's not running or taking up battery life. The ACTIVITY you start form your receiver might be, but it's not the receiver doing this. – Nathaniel D. Waggoner Mar 21 '14 at 15:43
  • I'm registering my receiver from the manifest and setting the intent filter for it to filter the phone state changed intent. Then in the receiver receiver I check if the intent is phone state ringin and if yes I start an activity. – Salivan Mar 21 '14 at 15:45
  • 2
    Ah ok, if you register in the manifest then it automatically handles the unregistration. – Nathaniel D. Waggoner Mar 21 '14 at 15:45
  • So it means this broadcast receiver is completely shut off after my code is done? – Salivan Mar 21 '14 at 15:49
  • 1
    Please see my edits, but yes. The broadcast receiver is completely shut off. It COULD be that the objects you instantiate don't get cleaned up, but that is a HUGE bug if that's the case, and I don't think it is. – Nathaniel D. Waggoner Mar 21 '14 at 15:50
  • 1
    Added another minor eddit to clarify some stuff. – Nathaniel D. Waggoner Mar 21 '14 at 16:09
  • Thank you very much for all this clarification. However, is it possible that this code could drain my battery or hold some system resources? If yes - how to release them and completely stop the processes started by this code? – Salivan Mar 21 '14 at 17:03
  • The answer to this is a pretty clear no. The activity you start might cause this, but the broadcast receiver itself is not the thing that's causing it. It's possible that the teleMgr might be causing it, but that would be a big bug in the way the the broadcast receiver is managed and I don't think it is the case. – Nathaniel D. Waggoner Mar 21 '14 at 17:21
  • 1
    It is possible that by kicking off the services, and the having your references to them removed when the BR is killed, that you don't ever properly shut down the services and thus they just keep running. I don't think this is happening, but it is possible. – Nathaniel D. Waggoner Mar 21 '14 at 17:28
  • But I am kicking off the servive with the LISTEN_NONE flag by the TelephonyMabager's listen() methon. – Salivan Mar 21 '14 at 17:36
  • 1
    Yea like I said - I doubt this is happening. Its the only possible thing I could see that could conceivably cause the battery drain your noticing though. – Nathaniel D. Waggoner Mar 21 '14 at 18:02
  • If you are still here I have one question. If you say that I'm using this broadcast receiver in a wrong way, then where should I complete the checking of the incoming call intent and starting the activity? Should I start a service in broadcast receiver onReceive() method and do all the work there? So the broadcast receiver would be only receiving the intent and startig a service. One line of code in its onReceive() method. – Salivan Mar 22 '14 at 10:27