Урок 83. Handler. Отложенные сообщения, удаление из очереди, Handler.Callback


В этом уроке:

- посылаем отложенные сообщения
- удаляем сообщения из очереди
- используем 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