В этом уроке:
- посылаем отложенные сообщения
- удаляем сообщения из очереди
- используем Handler.Callback для обработки сообщений
В прошлых уроках мы отправляли сообщения в очередь, а система сразу же доставала их и перенаправляла в Handler на обработку. Но мы можем настроить сообщение так, чтобы система отправило его на обработку не сразу, а с задержкой. Для этого используются методы sendEmptyMessageDelayed (если используете только what) и sendMessageDelayed (полное сообщение). В них мы можем указать паузу в миллисекундах. Система выждет эту паузу и только потом отправит сообщение в Handler.
Если вдруг поместили такое отложенное сообщение в очередь, а потом решили, что оно не должно уйти на обработку, то его можно из очереди удалить. Для этого используется метод removeMessages.
В прошлых уроках мы создавали свой Handler, и в его методе handleMessage кодили свой алгоритм обработки сообщений. Кроме этого способа Handler также может использовать для обработки сообщений объект, реализующий интерфейс Handler.Callback. У интерфейса всего один метод handleMessage – в нем и прописываем всю логику обработки сообщений. Я пока не встречал практической пользы от этой штуки, но все же разберемся, как ее можно использовать. Может когда и пригодится.
Создадим проект:
Project name: P0831_HandlerMessageManage
Build Target: Android 2.3.3
Application name: HandlerMessageManage
Package name: ru.startandroid.develop.p0831handlermessagemanage
Create Activity: MainActivity
strings.xml и main.xml не трогаем, они нам не нужны. Будем работать с логами.
Кодим MainActivity.java:
package ru.startandroid.develop.p0831handlermessagemanage; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; public class MainActivity extends Activity { final String LOG_TAG = "myLogs"; Handler h; Handler.Callback hc = new Handler.Callback() { public boolean handleMessage(Message msg) { Log.d(LOG_TAG, "what = " + msg.what); return false; } }; /** Called when the activity is first created. */ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); h = new Handler(hc); sendMessages(); } void sendMessages() { Log.d(LOG_TAG, "send messages"); h.sendEmptyMessageDelayed(1, 1000); h.sendEmptyMessageDelayed(2, 2000); h.sendEmptyMessageDelayed(3, 3000); } }
Мы создаем объект hc типа Handler.Callback. У него есть метод handleMessage, в котором мы будем обрабатывать сообщения. В нашем случае просто читаем атрибут what и выводим значение в лог.
В onCreate создаем handler, используя конструктор Handler (Handler.Callback callback). На вход передаем созданный ранее hc. И теперь Handler будет обрабатывать сообщения не сам, а перепоручит это объекту hc. Далее мы выполняем метод sendMessages , который кладет три сообщения в очередь сообщений. Для этого используется метод sendEmptyMessageDelayed. Это аналог знакомого нам метода sendEmptyMessage с прошлого урока. Он тоже заполняет в сообщении только атрибут what, но при этом он позволяет указать задержку в обработке сообщения. Т.е. сообщение будет извлечено из очереди и отправлено на обработку через указанное количество миллисекунд.
Итак, мы помещаем три сообщения:
1) what = 1, обработка через 1000 мс.
2) what = 2, обработка через 2000 мс.
3) what = 3, обработка через 3000 мс.
Замечу, что отсчет задержки начинается после помещения в очередь, а не после обработки предыдущего сообщения. Т.е. эти сообщения по отношению друг к другу сработают с интервалом в одну секунду.
Все сохраним и запустим приложение. В логе одна за другой будут появляться записи:
10:21:07.759: D/myLogs(332): send messages
10:21:08.786: D/myLogs(332): what = 1
10:21:09.765: D/myLogs(332): what = 2
10:21:10.776: D/myLogs(332): what = 3
Обратите внимание на время этих записей. Первое срабатывает через 1000 мс после помещения в очередь (send messages), второе - через две секунды, третье – через три.
Теперь попробуем удалить сообщение из очереди. Перепишем метод sendMessages:
void sendMessages() { h.sendEmptyMessageDelayed(1, 1000); h.sendEmptyMessageDelayed(2, 2000); h.sendEmptyMessageDelayed(3, 3000); h.removeMessages(2); }
Используем метод removeMessages, в котором указываем значение атрибута what. Этот метод находит в очереди сообщение с атрибутом what, равным 2, и удаляет его из очереди.
Все сохраняем, запускаем приложение. Смотрим лог:
10:24:49.916: D/myLogs(434): send messages
10:24:50.927: D/myLogs(434): what = 1
10:24:52.948: D/myLogs(434): what = 3
Как видим, сообщение с what = 2 не сработало.
А если будет несколько сообщений с одинаковым значением what? Система удалит первое попавшееся или все?
Проверим. Перепишем sendMessages:
void sendMessages() { Log.d(LOG_TAG, "send messages"); h.sendEmptyMessageDelayed(1, 1000); h.sendEmptyMessageDelayed(2, 2000); h.sendEmptyMessageDelayed(3, 3000); h.sendEmptyMessageDelayed(2, 4000); h.sendEmptyMessageDelayed(5, 5000); h.sendEmptyMessageDelayed(2, 6000); h.sendEmptyMessageDelayed(7, 7000); h.removeMessages(2); }
Будем помещать в очередь кучу сообщений. Из них несколько с what = 2. Проверим, какие удалит система.
Запускаем приложение и смотрим лог:
10:29:23.297: D/myLogs(467): send messages
10:29:24.372: D/myLogs(467): what = 1
10:29:26.307: D/myLogs(467): what = 3
10:29:28.364: D/myLogs(467): what = 5
10:29:30.332: D/myLogs(467): what = 7
Все сообщения с what = 2 были удалены. Не забывайте это. А то захотите удалить одно последнее сообщение, а система найдет все подходящие, ожидающие обработки, и снесет их.
У метода removeMessages есть еще реализация с использованием obj. Тут все так же, только система ищет для удаления из очереди сообщения с указанными атрибутами what и obj.
Если хотите запланировать полноценное сообщение, а не просто what, то используйте метод sendMessageDelayed – на вход даете сообщение и указываете задержку обработки.
Есть еще методы sendEmptyMessageAtTime и sendMessageAtTime. Они тоже позволяют указать задержку обработки. Но эта задержка будет отсчитана от времени последнего старта системы, а не от времени помещения в очередь. Если сообщение окажется просроченным на момент помещения в очередь, оно выполняется сразу.
На следующем уроке:
- работаем с Handler и Runnable
Присоединяйтесь к нам в Telegram:
- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.
- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance
- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня