Как обновить виджет, если связанный сервис убит? - PullRequest
2 голосов
/ 23 апреля 2011

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

Запись выполняется аудио движком, работающим в сервисе, переведенном в состояние переднего плана.

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

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

Пока все хорошо.

Теперь, если служба по какой-то причине убита, onDestroy () не вызывается, и у меня нет возможности отправить паузу или сообщение о выключении, чтобы кнопка записи отключилась. Результат: кнопка остается красной, в то время как запись остановлена.

Это худшее, что может случиться.

Почему? Пользователь смотрит на свой домашний экран, видит красную кнопку и думает, что запись все еще выполняется.

Это не похоже на воспроизведение музыки, когда пользователь может заметить, когда музыка перестает воспроизводиться через наушники ... Во время записи вы ничего не услышите, если она остановится.

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

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

Так как я могу быстро обновить виджет , если служба будет убита?

Ответы [ 3 ]

3 голосов
/ 20 июня 2011

Я нашел то, что считаю своим ответом на свой вопрос.

Я возвращаю START_STICKY из onStartCommand () в моем сервисе.

Если я вручную убиваю службу из DDMS, я получаю это в logcat:

I/ActivityManager( 2500): Process com.foo.bar (pid 21874) has died.
W/ActivityManager( 2500): Scheduling restart of crashed service com.foo.bar/.FooBarService in 5000ms

И через несколько секунд:

I/ActivityManager( 2500): Start proc com.foo.bar for service com.foo.bar/.FooBarService: pid=22036 uid=10107 gids={1015, 3003} 

Служба перезапускается,и это позволяет мне отправлять трансляцию с onStartCommand(), чтобы указать, что звуковой движок приостановлен (я не возобновляю запись автоматически).

Поставщик виджетов реагирует на эту трансляцию и соответствующим образом обновляет виджет.

Это работает на Froyo и решает мою проблему: пользователь не вводится в заблуждение при просмотре виджета.Запись остановлена ​​из-за сбоя двигателя, и виджет отражает это.

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

0 голосов
/ 26 мая 2015

Процесс службы Foreground будет убит, когда пользователь удалит его в списке недавних задач.

В момент пролистывания android вызовет (*) метод onTaskRemoved вашей службы.Вы можете обновить свой виджет прямо здесь:

@Override
public void onTaskRemoved(Intent rootIntent) {
    Log.i(TAG, "onTaskRemoved, stopping service");

    AppWidgetManager widgetManager       = AppWidgetManager.getInstance(this);
    ComponentName    widgetComponentName = new ComponentName(this, MyWidgetProvider.class);
    widgetManager.updateAppWidget(widgetComponentName, buildWidgetRemoteViews());

    stopSelf();
}

Если вы позвоните по номеру stopSelf внутри onTaskRemoved, служба завершит работу в обычном режиме: будет вызван onDestroy, и перезапуск не запланирован.
Если вы не вызовите stopSelf, Android завершит процесс и продолжит работу как при обычном уничтожении службы - в соответствии с onStartCommand возвращаемым значением:

  • START_NOT_STICKY - службане будет перезапущен
  • START_STICKY - сервис будет перезапущен с пустым намерением
  • ... и т. д.

Позвольте мне подчеркнуть это, даже если вы неПозвоните stopSelf, сервис перестанет работать сразу после выхода onTaskRemoved.Таким образом, вы не можете отправить трансляцию или запланировать что-то на Handler.Однако, если вы действительно хотите широковещательную рассылку, используйте AlarmManager:

final int requestCode = 101;
final AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
final PendingIntent clearWidget = PendingIntent.getBroadcast(
        this,
        requestCode,
        getClearWidgetIntent(),
        PendingIntent.FLAG_UPDATE_CURRENT);

// if broadcast arrives while process is shutting down, widget update may fail
// 100ms was always OK on devices I've tested and instantaneous for user
final long startDelay = 100;
alarmManager.set(AlarmManager.ELAPSED_REALTIME,
                 SystemClock.elapsedRealtime() + startDelay,
                 clearWidget);

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

Примечания:
*) на самом деле это может быть не такзвонил - см. документы

0 голосов
/ 23 апреля 2011

Так, как я могу быстро обновить виджет, если служба убита?

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

...