Моя деятельность запускает службу, которая запускает CountDownTimer. Таймер отправляет широковещательную рассылку активности по мере обратного отсчета. Действие обрабатывает широковещательные рассылки в методе onReceive BroadcastReceiver. Все это прекрасно работает.

Моя проблема возникает, когда следующие события происходят в таком порядке:

  1. Приложение остановлено (через onPause ())
  2. Таймер заканчивается
  3. Приложение возобновлено (через onResume ())

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

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

Для записи вот мой соответствующий код действий и услуг:

activity.java

// Start service
timerIntent.putExtra("totalLength", totalLength);
this.startService(timerIntent);

// ...

// BroadcastReceiver
private BroadcastReceiver br = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getExtras() != null && inSession) {
            session.setRemaining(intent.getExtras().getLong("millisUntilFinished"));
            updateProgress();
        }
    }
};

// ...

// onResume
@Override
public void onResume() {
    super.onResume();
    registerReceiver(br, new IntentFilter(TimerService.COUNTDOWN_TS));
}

service.java

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    long length = intent.getExtras().getLong("totalLength");

    countDownTimer = new CountDownTimer(length, 1000) {
        @Override
        public void onTick(long millisUntilFinished) {
            timerServiceIntent.putExtra("millisUntilFinished", millisUntilFinished);
            sendBroadcast(timerServiceIntent);
        }

        @Override
        public void onFinish() {
        }
    };

    countDownTimer.start();

    return super.onStartCommand(intent, flags, startId);
}

Как лучше всего обрабатывать широковещательные сообщения, отправленные службой, когда активность была остановлена?

3
Alex Johnson 5 Май 2016 в 14:43

4 ответа

Лучший ответ

Используйте BroadcastReceiver, чтобы сохранить последний полученный запрос (возможно, SharedPreferences) и проверить его при запуске Activity.

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

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

Предположим, что «XYZ» - это служба \ намерение \ что-либо, начиная отсчет времени, а «ABC» - это действие, отображающее прогресс. (ABC и XYZ могут быть одним и тем же действием, если вы этого хотите)

Требования: когда начинается обратный отсчет, я бы заставил XYZ сохранить время, когда обратный отсчет должен закончиться в SharedPreferences.

  1. ABC уже запущен, когда начинается обратный отсчет. Как сообщила Commonsware, модель Eventbus отлично подходит для обработки этого сценария, если XYZ и ABC выполняются в одном процессе. Просто запустите событие, чтобы прочитать значение предпочтения и отсчитать до указанного времени. Если пользователь закроет ABC и снова откроет его, сработает Сценарий 2.

  2. ABC не работает. Проверьте в OnResume, истекло ли время обратного отсчета. Если нет, настройте ABC, чтобы снова отобразить обратный отсчет. Если обратный отсчет не активен, сделайте что-нибудь еще.

Если вам также нужно что-то сделать по истечении обратного отсчета, независимо от того, активен ли у вас пользовательский интерфейс, то снова предложение Commonsware о AlarmManager идеально подходит.

3
Kuffs 5 Май 2016 в 13:48

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

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

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

Также, пожалуйста, придерживайтесь пар жизненного цикла. onResume() не является эквивалентом onStop(). onStart() является эквивалентом onStop(); onResume() является эквивалентом onPause(). Инициализация чего-либо в одной паре (например, onResume()) и очистка в другой паре (например, onStop()) сопряжены с риском ошибок двойной инициализации или двойной очистки.

1
CommonsWare 5 Май 2016 в 12:05

Как лучше всего обрабатывать широковещательные сообщения, отправленные службой, когда активность была остановлена?

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

http://developer.android.com/reference/android/content/Context.html#sendStickyBroadcast(android.content.Intent)

Однако они устарели с уровня API 21 из-за проблем с безопасностью.

1
therobotichumanoid 5 Май 2016 в 12:32

Вместо обычной трансляции вы можете использовать упорядоченную трансляцию (отправляемую с помощью Context.sendOrderedBroadcast). Для этого наряду с определением BroadcastReceiver в вашей деятельности вам необходимо было определить BroadcastReceiver в своем манифесте с тем же фильтром намерений. Единственное изменение заключается в том, что при регистрации BroadcastReceiver в вашей деятельности вам необходимо установить высокий приоритет, чтобы, когда ваша активность выполняется и BroadcastReceiver активности зарегистрирован, он вызывается первым, а внутри onReceive этого BroadcastReceiver вы можете использовать abortBroadcast для вызова BroadcastReceiver, который определено в вашем манифесте Android. Теперь, когда ваша деятельность не выполняется, будет вызываться BroadcastReceiver, определенный в вашем манифесте Android. Таким образом, вы можете иметь статус и, если хотите, вы можете отображать обновления для пользователя посредством уведомления, даже если ваша активность не выполняется.

0
Rajen Raiyarela 5 Май 2016 в 12:30