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

Как получить привязанный к Android сервис, чтобы выжить при перезапуске конфигурации

У меня есть приложение для Android с некоторым глобальным состоянием (включая некоторые большие SoundPool s), которые требуют очистки, поэтому, следуя ответам на мой предыдущий вопрос, я пытаюсь обработать это с помощью Service.

В настоящее время я использую связанную службу, которую каждое действие связывает/разворачивает в onStart/onStop, а когда все действия останавливаются, служба становится несвязанной и onDestroy вызывается в службе, позволяя мне освободить SoundPools.

Так как жизненный цикл активности преднамеренно перекрывается (новая активность onStart срабатывает до того, как старая стрелка срабатывает onStop), при навигации между действиями всегда есть хотя бы одна активность, и служба остается в живых.

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

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

4b9b3361

Ответ 1

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

Существует несколько способов приблизиться к этому, но самый простой, который я использовал, - это иметь "запущенный" сервис, который решает, когда нужно закрыться. Мои действия привязываются/отключаются к сервису, и после установленной задержки (я использовал 1 минуту) служба отключится, если дальнейшие действия не будут связаны - это охватывает, если пользователь перестает использовать приложение плюс, если есть любые ошибки со смертельным исходом.

Таймер отключения запланирован в пределах onUnbind() и отменяется в onStartCommand(), onBind(), onRebind(). Если он срабатывает, он отключает службу вниз, что, в свою очередь, вызывает очистку управляемого состояния в сервисе onDestroy().

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

public class LocalStateService extends Service {

    /** The binder to give to clients. */
    private final IBinder binder = new LocalStateBinder();

    /** Used for time-delayed shutdown. */
    private final Handler handler = new Handler();

    /**
     * Called before starting or the first binding.
     */
    @Override
    public void onCreate() {
        // initialise state...
    }

    /**
     * Called when this service is explicitly started.
     * @param intent    The intent passed on starting, unused
     * @param flags     Startup flags, unused
     * @param startId   Identifies each start request 
     * @return Desired restart behaviour
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        cancelShutdown();

        // if killed, we would like Android to restart this service, but don't bother re-delivering
        // the original intent used to start the service
        return START_STICKY;
    }

    /**
     * Called when the first client binds.
     * @param intent        The intent passed on binding
     * @return The binding to use
     */
    @Override
    public IBinder onBind(Intent intent) {
        cancelShutdown();
        return binder;
    }

    /**
     * Called when the first of previous clients re-binds.
     * @param intent        The intent passed on binding
     */
    @Override
    public void onRebind(Intent intent) {
        cancelShutdown();
    }

    /**
     * Called when all clients have unbound.
     * @param intent        The first intent originally passed on binding
     * @return Whether this service should be notified of rebinding
     */
    @Override
    public boolean onUnbind(Intent intent) {

        // post a callback to be run in 1 minute
        handler.postDelayed(delayedShutdown, 1000L * 60);

        // we do want onRebind called when clients return
        return true;
    }


    @Override
    public void onDestroy() {
        // state cleanup...
    }

    private Runnable delayedShutdown = new Runnable() {

        @Override
        public void run() {
            LocalStateService.this.stopSelf();
        }

    };

    /**
     * Cancel any shutdown timer that may have been set.
     */
    private void cancelShutdown() {
        // remove any shutdown callbacks registered
        handler.removeCallbacks(delayedShutdown);
    }
}

Вместо этого из моего Application моя основная деятельность вызывает startService(..) в onCreate(), так как это будет работать как для первоначального запуска, так и когда пользователь вернется к использованию приостановленного приложения (где служба может или может не решили закрыть себя).

Каждое действие затем связывает и отвязывает в соответствии с нормальным.

Я обнаружил, что:

  • При перемещении между действиями никакой обратный вызов службы не запускается. Поскольку жизненный цикл активности перекрывается, это вторичные bind/unbinds

  • Когда действие перезапускается (например, поворот экрана), служба получает onUnbind(), а затем вызов onRebind()

  • При приостановке приложения (например, нажмите "домой" из основного действия) или при завершении (например, нажмите "назад" из основного действия) служба получает onUnbind(), тогда срабатывает таймер.