Thursday, April 19, 2018

Starting a service when the device's screen is unlocked in Android

Leave a Comment

I would like to run my service when the screen is unlocked and stop it when the screen is locked. I looked into these answers and implemented them. However, when I lock my screen the service stops as required, but when I unlock the screen it doesn't starts again.

The code for to run this service is as follows:

public class PhonePositionService extends Service { @Override     public void onCreate() {     //ADDED CODE     IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);     filter.addAction(Intent.ACTION_SCREEN_OFF);     BroadcastReceiver mReceiver = new BootCompletedIntentReceiver();     registerReceiver(mReceiver, filter);     {...}     } @Override     public int onStartCommand(Intent intent, int flags, int startId) {     int icon = R.drawable.ic_stat_bright;     startForeground(NOTIFICATION_ID, getCompatNotification(icon));     if (backgroundThread != null) {       backgroundThread.start();     }     return START_STICKY;   } } 

In some other file, my broadcast receiver code is as follows:

public class BootCompletedIntentReceiver extends BroadcastReceiver {  @Override  public void onReceive(Context context, Intent intent) {    String action = intent.getAction();    if(Intent.ACTION_SCREEN_ON.equals(action)) {      // start the service      Intent pushIntent = new Intent(context, PhonePositionService.class);      context.startService(pushIntent);    } else if(Intent.ACTION_SCREEN_OFF.equals(action)) {      // stop the service      Intent pushIntent = new Intent(context, PhonePositionService.class);      context.stopService(pushIntent);    }   if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {     Intent pushIntent = new Intent(context, PhonePositionService.class);     context.startService(pushIntent);   }  } } 

Then why is it so that when I unlock my phone the service does not start again?

2 Answers

Answers 1

First of all its not possible to detect if user has unlocked the phone until you have Admin Privilages. The issue you are facing is because you are stopping the PhonePositionService using stopService() when Screen is OFF:

public class BootCompletedIntentReceiver extends BroadcastReceiver {  @Override  public void onReceive(Context context, Intent intent) {    String action = intent.getAction();    if(Intent.ACTION_SCREEN_ON.equals(action)) {      // start the service      Intent pushIntent = new Intent(context, PhonePositionService.class);      context.startService(pushIntent);    } else if(Intent.ACTION_SCREEN_OFF.equals(action)) {      // stop the service      Intent pushIntent = new Intent(context, PhonePositionService.class);      //This will stop the service      context.stopService(pushIntent);    }   if ("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {     Intent pushIntent = new Intent(context, PhonePositionService.class);     context.startService(pushIntent);   }  } } 

It would lead to some memory leak since BroadcastReceiver is not unregistered.

It is discouraged to keep service running in foreground for forever since it could lead to (fast) battery drainage.

I would recommend you to find other alternatives. But if your core functionality is impacted and you still want to proceed you can do following work around:

Make BootCompletedIntentReceiver as inner class for PhonePositionService. And instead of starting and stopping the service you perform your action directly.

public class PhonePositionService extends Service {    @Override    public void onCreate() {        //ADDED CODE        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);        filter.addAction(Intent.ACTION_SCREEN_OFF);        BroadcastReceiver mReceiver = new BootCompletedIntentReceiver();        registerReceiver(mReceiver, filter);        ...     }     ...     private class BootCompletedIntentReceiver extends BroadcastReceiver {        @Override        public void onReceive(Context context, Intent intent) {          String action = intent.getAction();          if(Intent.ACTION_SCREEN_ON.equals(action)) {           //DO action for SCREEN_ON          } else if(Intent.ACTION_SCREEN_OFF.equals(action)) {           //Do action for SCREEN_OFF          }         }    } } 

Answers 2

That's because you are registering your receiver inside your service, which is a component like an activity that has a lifecycle and when it's destroyed your broadcast listener goes with it too. Actually I'm guessing you are leaking your Service here too, because you didn't unregister it which is another problem.

Since API 26 aka Oreo you cannot register you broadcast listener inside AndroidManifest.xml, If you want to target API 26+ your best bet is to register it in your Application class, but be aware when the system kills your app you will lose your broadcast listener too. However if you use a foreground service the system won't kill your service.

Edit: Here is an example:

import android.app.Application; import android.content.Intent; import android.content.IntentFilter;  /**  * @author : M.Reza.Nasirloo@gmail.com  * Created on: 2018-04-12  */ public class App extends Application {      @Override     public void onCreate() {     super.onCreate();     IntentFilter filter = new IntentFilter();     filter.addAction(Intent.ACTION_SCREEN_OFF);     filter.addAction(Intent.ACTION_SCREEN_ON);     registerReceiver(new BootCompletedIntentReceiver(), filter);     } } 

Don't forget to set this class inside your manifest file.

<application     android:name=".App"     android:allowBackup="true" 

Note you need a foreground service to be run always to not miss the events.

PS: There's another solution which is a bit hacky, target a lower API version and register your listener inside the manifest file.

Looks like targeting a lower API is out of table. It seems google knew some developers would use it to get around this.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment