Android: служба уничтожается при повороте дисплея

У меня есть служба, которая работает в отдельном процессе. Я обнаружил, что после того, как поток пользовательского интерфейса основного процесса выходит из onDestroy(), моя служба уничтожается, хотя я предоставил контекст приложения с привязкой и указал BIND_AUTO_CREATE.

В потоке пользовательского интерфейса моего основного процесса onCreate() у меня есть этот код привязки:

Intent intent = new Intent(mAppContext, MyService.class);
mAppContext.bindService(intent, mMyServiceConnection, Context.BIND_AUTO_CREATE);

В потоке пользовательского интерфейса моего основного процесса onDestroy() у меня есть этот отвязывающий код:

mAppContext.unbindService(mMyServiceConnection);

Обратите внимание, что я никогда не вызываю stopService().

Документация Android для bindService() говорит:

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

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

Я подумал, что, возможно, контекст приложения умирает с помощью onDestroy(). Вот что говорится в документации Android для getApplicationContext():

Возвращает контекст единого глобального объекта Application текущего процесса.

Если контекст приложения умирает с помощью onDestroy(), то я думаю, что у Android есть большая проблема. Проблема в том, что когда дисплей поворачивается, вызывается onDestroy() (за которым сразу же следует onCreate()). Таким образом, эффект таков, что при повороте дисплея -- а в моем случае это происходит довольно часто! -- моя служба всегда выходит.

Обратите внимание, что pid процесса моего приложения никогда не меняется, т.е. это один и тот же процесс. Это важно в свете документации для getApplicationContext(), в которой указано «текущий процесс».

Вот что показывают мои журналы отладки:

04-03 05:15:12.874: DEBUG/MyApp(841): main onDestroy
04-03 05:15:12.895: DEBUG/MyApp(847): служба onUnbind
04-03 05:15:12.895 : DEBUG/MyApp(847): служба onDestroy
04-03 05:15:12.934: DEBUG/MyApp(841): main onCreate
04-03 05:15:12.966: DEBUG/MyApp(847): служба onCreate
03-04 05:15:12.975: DEBUG/MyApp(847): служба onBind

Итак, мои вопросы:

1) Правильно ли я понимаю привязку/отвязку?

2) Есть ли способ, чтобы мой сервис не уничтожался при вызове onDestroy() потока пользовательского интерфейса?

Хитрость для вопроса № 2 — никогда не отвязываться. Но мне это не нравится, потому что тогда я пропускаю привязку каждый раз, когда вызывается onDestroy(). Я мог бы «вспомнить», что у меня есть одна утечка привязки и утечка только этой, но тогда у меня есть каскадные хаки, и это действительно уродливо.


person Syrinx    schedule 03.04.2011    source источник


Ответы (3)


1) Да, я думаю, что вы правильно поняли (я говорю, что думаю, потому что я думаю, что понимаю, о чем вы говорите ;-)). Используемый вами флаг означает «запускать эту службу автоматически, если кто-то пытается связать с ней, и поддерживать ее работу до тех пор, пока кто-то с ней связан, но как только к ней никто не привязан, не стесняйтесь ее убивать».

2) Проверьте флаг START_STICKY, как описано здесь. Это должно позволить вам запустить службу и поддерживать ее работу независимо от того, что происходит с вызывающим Context.

В общем, onDestroy() означает, что ваша активность вот-вот будет уничтожена. Когда вы поворачиваете дисплей, Activity уничтожается и создается заново. Вы несете ответственность за сохранение любого состояния в Bundle в соответствующем методе, а затем его восстановление в onCreate().

person Chris Thompson    schedule 03.04.2011
comment
Я уже возвращаю START_STICKY из onStartCommand() моей службы. Я должен был упомянуть об этом во вступительном посте. - person Syrinx; 03.04.2011
comment
Запуск службы с помощью startService(intent) перед привязкой должен предотвратить перезапуск службы при уничтожении активности. - person Nicolas Girardin; 21.06.2014

Ваша служба убита:

  1. если в стеке есть второй Activity?
  2. если вы обрабатываете изменения конфигурации?

Зачем вам нужен ваш сервис, чтобы остаться в живых после того, как ваше приложение было уничтожено?

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

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

О "втором" Activity: Image вы начинаете активность A, а затем активность B. Теперь вы поворачиваете экран, пока отображается B, что приводит к перезапуску B. Будет ли A перезапущен в этот момент? Я не уверен, но у меня есть подозрение, что A будет иметь тенденцию оставаться в живых и поддерживать ваше приложение во время изменения ориентации. Это может быть еще одной стратегией поддержания вашего сервиса в рабочем состоянии, если вы к этому стремитесь.

person Matthew Willis    schedule 03.04.2011
comment
1. Я не уверен, что вы имеете в виду. Как вторая активность может находиться в стеке, когда она находится внутри onDestroy() для основного потока пользовательского интерфейса? 2. Что вы подразумеваете под изменениями конфигурации? --- Почему я не могу нажать Enter в этом текстовом поле? --- - person Syrinx; 03.04.2011
comment
Извините за отсутствие абзацев. Я не могу использовать ввод в этом текстовом поле? Чтобы добавить к тому, что я написал ранее... Мне все равно, завершится ли мой сервис. Но чего я не хочу, так это выхода службы, а затем немедленного перезапуска, что происходит при повороте дисплея. - person Syrinx; 03.04.2011

служба уничтожается только тогда, когда выполняются оба следующих условия:

  1. Все вызовы bindService() были сопоставлены с соответствующими вызовами unbindService().
  2. Если кто-то вызывал startService(), кто-то также вызывал stopService() или службу с именем stopSelf().

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

это дает отличное решение, которое также является достаточно правильным и безопасным!

скажем, есть 2 действия («просмотрщик» и «чат» в этом примере), которым нужна служба, если и bindService, и startService. Также с помощью связующего они обновляют «viewer_connected» и «chat_connected» во время onStart и onStop.

Затем служба запускает цикл в потоке, который делает следующее:

public isRunning = true;
while (isRunning) {

    if (viewer_connected) {
        // send update to viewer activity
    }

    if (chat_connected) {
        // send update to chat activity
    }

    try {
        Thread.sleep(5000);
    } catch (Exception e) { isRunning=false; }

    // 3 second timeout before destroying service
    if (!viewer_connected && !chat_connected) {
        try { Thread.sleep(3000); } catch (Exception e) { isRunning=false; }

        if (!viewer_connected && !chat_connected) isRunning=false;
    }

}
stopSelf();

Это работает, потому что ему нужны как действия для отвязки, так и служба для остановки себя(), прежде чем она уничтожит службу, что означает, что есть тайм-аут, прежде чем служба будет уничтожена.

person Smellymoo    schedule 17.03.2016