5

Issues

Service is NOT running always even after I have used START_STICKY.

Sometimes I dont get any Toast Action for Outgoing call, is that mean service stops after some time ?

My Requirment

Application shows a Toast whenever user makes a outgoing call from the phone. For this I am using a BroadcastReceiver to tap the call action and a service (to run Receiver always). once I start this activity, it starts showing toast when a outgoing call get initiated ..but not Always.

Below is the complete code -

MainActivity.class

public class MainActivity extends Activity 
{
    CallNotifierService m_service;
    boolean isBound = false;

    private ServiceConnection m_serviceConnection = new ServiceConnection() 
    {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) 
        {
            m_service = ((CallNotifierService.MyBinder)service).getService();
            Toast.makeText(MainActivity.this, "Service Connected", Toast.LENGTH_LONG).show();
            isBound = true;
            Intent intent = new Intent(MainActivity.this, CallNotifierService.class);
            startService(intent);
        }

        @Override
        public void onServiceDisconnected(ComponentName className) 
        {
            m_service = null;
            isBound = false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent(this, CallNotifierService.class);
        bindService(intent, m_serviceConnection, Context.BIND_AUTO_CREATE);
    }
    .
    .
    .
}

CallNotifierService.class

public class CallNotifierService extends Service 
{
    private final IBinder myBinder = new MyBinder();
    private static final String ACTION_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL";

    private CallBr br_call;

    @Override
    public IBinder onBind(Intent arg0) 
    {
        return myBinder;
    }

    @Override
    public void onDestroy() 
    {
        Log.d("service", "destroy");
        this.unregisterReceiver(this.br_call);
        Toast.makeText(CallNotifierService.this, "Receiver Un-Registered", Toast.LENGTH_LONG).show();
        super.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
        final IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_OUTGOING_CALL);
        this.br_call = new CallBr();
        this.registerReceiver(this.br_call, filter);
        Toast.makeText(CallNotifierService.this, "onStartCommand Called", Toast.LENGTH_LONG).show();
        return START_STICKY;
    }

    public class MyBinder extends Binder 
    {
        CallNotifierService getService() 
        {
            return CallNotifierService.this;
        }
    }

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

        @Override
        public void onReceive(Context context, Intent intent) 
        {
            Toast.makeText(context, "Action:"+intent.getAction(), Toast.LENGTH_LONG).show();
        }
    }
}

2 Answers 2

5

You are getting the wrong approach here, by mixing a simple idea (that would work if done correctly) with more complicated ideas (that cannot work).

Keep in mind: services are not "always running" components, even when using START_STICKY.

The Android system will not hesitate to kill your service if it needs memory somewhere else. START_STICKY only means that the Android system will re-start your service when it can, calling onStartCommand as specified in the documentation.

If you need a service to really stick around, then you must use a foreground service. But it will have consequences on the UI (annoying notification icon always showing), and battery life, and you do not need this here.

Now here is the magic trick: your app does not need to be running for your BroadcastReceiver to work. All you need to do is to register it in your AndroidManifest.xml with the correct intent-filter:

<receiver android:name=".broadcastreceivers.CallBroadcastReceiver">
    <intent-filter>
        <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
    </intent-filter>
</receiver>

(also make sure your app has the required permissions, namely PROCESS_OUTGOING_CALLS).

Then all you need in code is:

public class CallBroadcastReceiver extends BroadcastReceiver 
{
    @Override
    public void onReceive(Context context, Intent intent) 
    {
        Toast.makeText(context, "Action: " + intent.getAction(), Toast.LENGTH_LONG).show();
    }
}

No activity (except to ask for the PROCESS_OUTGOING_CALLS permission on Android 6+), no service, nothing. Simple and battery-efficient !

12
  • will it work always without service if I register via AndroidManifest.xml ?
    – Bharat
    Commented Feb 26, 2016 at 10:17
  • @ personne3000 evenif I close my app will it keep working ? unless I reboot my phone .... ?
    – Bharat
    Commented Feb 26, 2016 at 10:19
  • @Bharat Even if you reboot your phone and do not start your app, it will work. By adding this intent-filter in your manifest you essentially tell the system "start this BroadcastReceiver in my app every time the user has an outgoing call", so this is completely separate from your app's lifecycle: your app is registered when it is installed, not when you run it. Commented Feb 26, 2016 at 10:29
  • 1
    As discussed there, you apparently need to at least start your app once from the launcher. You should provide a UI so the user can approve the PROCESS_OUTGOING_CALLS permission on Android 6+ anyway, when you make the switch to API 23+ (see docs on dangerous permissions) Commented Feb 27, 2016 at 13:03
  • 1
    Registering by the manifest is the most reliable way, if that is what you mean. You can still disable/enable the BroadcastReceiver in code too developer.android.com/training/monitoring-device-state/… Commented Feb 28, 2016 at 0:46
2

The service does get re-created, not not re-started. If you override the onCreate and do a Log.d or a Toast, you will see that it gets called after your app is destroyed.

So the trick to keep it running after it is recreated is to do your code on the onCreate method and use the onStartCommand just to return START_STICKY.

Not the answer you're looking for? Browse other questions tagged or ask your own question.