Как синхронизировать состояние кнопки переключения с Foreground Service? - PullRequest
7 голосов
/ 21 марта 2019

У меня есть служба переднего плана, которую я запускаю и останавливаю с помощью кнопки переключения в действии.Чтобы отслеживать, работает ли Служба или нет, я использовал переменную, которую я храню в SharedPreference.

Вот поток

Когдапользователь включает кнопку переключения

startService()     start background work  
makeForeGround()              
bindService()      to get update

Когда пользователь отключает кнопку переключения

unbindService()    to stop getting updates    
stopForeGround()          
stopService()      stop background work  

Когда пользователь покидает действие

unbindService(); To stop getting the update. 
                 But the service is running for background work

Когда пользователь снова входит в действие

if(Service is running)
Show toggle button state as enabled 
bindService() 

Я достиг всего этого.

Только проблема Iлицо таково:

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

Как получить уведомление о том, что служба была убита ОС.(Потому что onDestroy () не вызывается в этом случае)?Я также прошел через Как проверить, работает ли сервис на Android? , но, к сожалению, предоставляемые решения и методы устарели.

Ответы [ 4 ]

2 голосов
/ 24 марта 2019

Просто обходной путь.Не общее решение.

Я переработал.Теперь поток

Вход в действие

bindService()

Выход из действия

unbindService()

ВключитьПереключить

startService()

Отключить Переключить

stopService()

Как отслеживать, работает ли служба?

Для этого,Я использую переменную типа int SERVICE_START_COUNTER в сервисе и увеличиваю ее значение в onStartCommand ().И я получаю SERVICE_START_COUNTER, когда Деятельность связывается с обслуживанием.Основываясь на значении счетчика, я могу различить, работает служба или нет

public boolean isServiceRunning(int SERVICE_START_COUNTER)
{
    return SERVICE_START_COUNTER == 0 ? false : true;
}

. И на основе этого значения я написал метод, который синхронизирует пользовательский интерфейс.

public void syncUI(boolean isServiceRunning)
{
   setToggleState(isServiceRunning);
   setAniamtionState(isServiceRunning);
}

Значение SERVICE_START_COUNTER в разных случаях?

1) Введите действие впервые (переключатель все еще отключен)

onBind() // SERVICE_START_COUNTER = 0

2) Ввести активность в первый раз (пользователь включает переключатель)

onBind()           // SERVICE_START_COUNTER = 0
onStartCommand()   // SERVICE_START_COUNTER = 1

3) Повторно ввести активность во второй раз (тумблер включен)

onBind()           // SERVICE_START_COUNTER = 1

4) Служба уничтожена ОС.Повторно введите действие

onBind()           // SERVICE_START_COUNTER = 0

Почему это не общее решение?

Поскольку привязка требует времени (потому что onServiceConnected ()) вызывается асинхронно.Следовательно, Результат может быть недоступен сразу.Для этого я сначала проверяю статус службы в Splash Activity и обновляю аналогичный счетчик в классе Utility.

2 голосов
/ 21 марта 2019

Вам следует подумать о том, чтобы подойти к вашей проблеме с другой стороны.

Попытка сделать вывод, когда система убила вашу Service, является проблематичной.Приложения (Services) не уведомляются, когда они завершаются, поэтому обнаружить это условие сложно.Однако выяснить, когда Service начался, тривиально (onCreate()).Это ключ к правильному подходу.

Понятно, что вы понимаете, что правильный способ обмена информацией между Activity и Service - через привязку.Это другая важная часть решения.

РЕШЕНИЕ

Вы должны продлить срок службы вашего Service.Прямо сейчас кажется, что вы создаете Service для явной цели выполнения длительной задачи.Service создается, когда вы хотите, чтобы задача запускалась, и завершается, как только она заканчивается.Другими словами, область действия Service и «задача» одинаковы.

Вместо этого вы должны сделать «задачу» дочерней по отношению к Service;Service должно существовать несколько дольше, чем «задача».В частности, Service должен быть доступен при каждом запуске Activity (если только для передачи информации о состоянии «задачи» Activity).

Всякий раз, когдаActivity запускается, он должен привязаться к Service и зарегистрировать слушателя с помощью Service.Service вызывает этого слушателя всякий раз, когда A.) слушатель вновь регистрируется, или B.) «задача» изменяет состояния (запускает или останавливает).Таким образом, Activity всегда мгновенно информируется об изменениях состояния, если он связан с Service.

Когда Activity хочет, чтобы «задача» изменила состояния, он должен сообщитьService через привязку (или, возможно, вызов startService(), даже если Service уже запущен).Service затем запускает / останавливает фоновый поток (или что-то еще, что вы делаете для выполнения «задачи») и уведомляет любых слушателей.

Если Service запускает «задачу»в течение некоторого времени, и активность внезапно появляется, тогда Service ясно знает, что «задача» существует, и Activity получит эту информацию через слушателя, как только она свяжется.

Когда Activity останавливается, он должен очиститься, отменив регистрацию слушателя и отсоединив его от Service.

. При таком подходе устраняется необходимость в SharedPreferences или для обнаружения, когда Serviceостановился.Всякий раз, когда создается Service, очевидно, что «задание» не выполняется, поэтому Service всегда будет знать правильный ответ, который нужно дать Activity.А поскольку Service должен быть создан до того, как он сможет связываться с Activity, проблем с порядком операций нет.

0 голосов
/ 30 марта 2019

Попробуйте переопределить метод onTaskRemoved () вашей службы и измените значение переключателя на false в методе. Проверьте это Документация

0 голосов
/ 30 марта 2019

Ваша проблема вызвана тем, что вы используете сервис Bounded Foreground. Смотри: https://developer.android.com/guide/components/bound-services

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

Как правило, существование вашего сервиса зависит от того, привязан он или нет.

Альтернатива - не использовать метод Bind, а вместо этого использовать Context.startService () или Context.startForegroundService (), чтобы запустить службу и позволить ей работать вечно. Примерно так:

 Intent intent = new Intent(getApplicationContext(), ForegroundService.class);
 intent.setAction(ForegroundService.ACTION_START_FOREGROUND_SERVICE);
 startService(intent);

Где класс ForegroundService выглядит следующим образом:

public class ForegroundService extends Service {

private static final String TAG_FOREGROUND_SERVICE = "FOREGROUND_SERVICE";
public static final String ACTION_START_FOREGROUND_SERVICE = "ACTION_START_FOREGROUND_SERVICE";
public static final String ACTION_STOP_FOREGROUND_SERVICE = "ACTION_STOP_FOREGROUND_SERVICE";

public ForegroundService () {
}

@Override
public IBinder onBind(Intent intent) {
    // TODO: Return the communication channel to the service.
    throw new UnsupportedOperationException("Not yet implemented");
}

@Override
public void onCreate() {
    super.onCreate();
    Log.d(TAG_FOREGROUND_SERVICE, "My foreground service onCreate().");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if(intent != null)
    {
        String action = intent.getAction();

        switch (action)
        {
            case ACTION_START_FOREGROUND_SERVICE:
                startForegroundService();
                break;
            case ACTION_STOP_FOREGROUND_SERVICE:
                stopForegroundService();
                break;
        }
    }
    return super.onStartCommand(intent, flags, startId);
}

private void startForegroundService()
{
    // Build the notification.
    // ... lot of code ...
    Notification notification = builder.build();

    // Start foreground service.
    startForeground(1, notification);
}

private void stopForegroundService()
{
    // Stop foreground service and remove the notification.
    stopForeground(true);

    // Stop the foreground service.
    stopSelf();
}
}

В этом случае вам не нужно синхронизировать состояние сервиса, потому что он будет активен как можно дольше и остановится только при вызове stopSelf (). Служба не остановится, даже когда приложение перейдет в фоновый режим.

Кроме того, вы можете использовать методы широковещательной передачи результатов, liveata или EventBus для синхронизации / обмена данными между службой и действиями.

Надеюсь, этот ответ поможет. Я прошу прощения, если это не то, что вы ищете. Это просто идея.

...