Запуск службы, когда экран устройства разблокирован на Android

Я хотел бы запустить свою службу, когда экран разблокирован и остановится, когда экран заблокирован. Я изучил эти ответы и реализовал их. Однако, когда я блокирую свой экран, служба останавливается по мере необходимости, но когда я разблокирую экран, он не запускается снова.

Код для запуска этой службы следующий:

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;
  }
}

В другом файле мой код приемника вещания выглядит следующим образом:

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);
  }
 }
}

Тогда почему это так, что когда я разблокирую свой телефон, служба не запускается снова?

Ответ 1

Прежде всего, невозможно обнаружить, если пользователь разблокировал телефон до тех пор, пока у вас не будет Admin Privilages. Проблема, с которой вы сталкиваетесь, заключается в том, что вы останавливаете PhonePositionService с помощью stopService() когда экран выключен:

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);
  }
 }
}

Это приведет к некоторой утечке памяти, поскольку BroadcastReceiver не будет незарегистрирован.

Не рекомендуется поддерживать работу на переднем плане навсегда, так как это может привести к (быстрому) дренажу батареи.

Я бы рекомендовал вам найти другие альтернативы. Но если ваша основная функциональность затронута, и вы все еще хотите продолжить, вы можете выполнить следующую работу:

Сделайте BootCompletedIntentReceiver качестве внутреннего класса для PhonePositionService. И вместо того, чтобы запускать и останавливать службу, вы выполняете свое действие напрямую.

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
         }

       }
   }
}

Ответ 2

Это потому, что вы регистрируете свой приемник внутри своей службы, который является компонентом, подобным активности, которая имеет жизненный цикл, и когда она уничтожает ваш широковещательный прослушиватель, тоже идет с ним. На самом деле, я предполагаю, что вы тоже просачиваете свое обслуживание, потому что вы не отменили регистрацию, это еще одна проблема.

Поскольку API 26 aka Oreo вы не можете зарегистрировать прослушиватель вещания в AndroidManifest.xml, если вы хотите настроить таргетинг на API 26+, лучше всего зарегистрировать его в своем классе Application, но помните, когда система убивает ваше приложение, вы потеряете свой широковещательный прослушиватель тоже. Однако, если вы используете службу переднего плана, система не будет убивать вашу службу.

Изменить: Вот пример:

import android.app.Application;
import android.content.Intent;
import android.content.IntentFilter;

/**
 * @author : [email protected]
 * 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);
    }
}

Не забудьте установить этот класс внутри вашего файла манифеста.

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

Обратите внимание: вам нужно, чтобы служба переднего плана выполнялась всегда, чтобы не пропустить события.

PS: Еще одно решение, немного взломанное, нацелено на более низкую версию API и регистрирует ваш слушатель внутри файла манифеста.

Похоже, что таргетинг на более низкий API отсутствует. Кажется, Google понимает, что некоторые разработчики будут использовать его, чтобы обойти это.