В этом топике рассматривается еще один способ синхронизации потоков с использованием Intent, BroadcastReceiver и Handler. Метод этот может показаться очень громоздким, но тем не менее вполне удобным, особенно если в приложении используется достаточно много ассинхронных потоков. Например, если реализуется сервис, который общается с вебсервисом в фоновом режиме. Еще один пример – приложение, которое портируется сразу на несколько платформ и большую часть кода работы с API можно совместно использовать и в Android, и в J2ME, и в Windows Phone 7.
План действий
Интенты
Интенты – абстрактные описания операций для исполнения (по документации). В приложении удобно организовать хранение всех констант с действиями и параметрами интентов в одном статическом классе. В нашем случае для примера будем использовать два действия – страница списка загружена и картинка загружена.
Рассылка интентов
Отправку интентов можно сделать через методы приложения: доступно из любого класса, не требуется передавать и хранить ссылку на контекст.
Поэтому рассылка интентов выглядит очень просто:
В случае необходимости в интенте можно передать дополнительные параметры, например ID картинки. Для этого используем метод putExtra.
Перехват интентов
В активити интенты перехватываем с помощью анонимного экземпляра BroadcastReceiver. Устанавливаем его в момент перехода активити на передний план и убираем, когда активити уходит с экрана.
Сами события передаем в анонимный хендлер, поскольку обработка событий в активити чаще всего связано с обновлением компонентов на экране и требует исполнения в UI треде. Дополнительный плюс такого решения – можно обновлять компоненты с задержкой и отсекать часть событий, чтобы не вызывать мерцания на экране, например если каждый элемент списка содержит картинку, которая подгружается в отдельном треде (обратите внимание на методы removeMessages и sendEmptyMessageDelayed).
Приведенный здесь метод выглядит достаточно громоздким, но если учесть что в большинстве активити возможно потребуется перехват событий от тредов, то целесообразно вынести этот функционал в базовую активити и остальные наследовать от этого базового класса. Сам код внести снипетом в Eclipse и не вводить каждый раз.
План действий
- Подготавливаем статический класс с названиями интентов.
- Создаем методы в классе-наследнике Application для отправки интентов.
- Из тредов посылаем интенты событий.
- Перехватываем интенты в активити с помощью BroadcastReceiver.
- Передаем в Handler события и обрабатываем их.
Интенты
Интенты – абстрактные описания операций для исполнения (по документации). В приложении удобно организовать хранение всех констант с действиями и параметрами интентов в одном статическом классе. В нашем случае для примера будем использовать два действия – страница списка загружена и картинка загружена.
public final class Intents { // загрузилась очередная страница списка public final static String LIST_PAGE_RECEIVED = "com.olsoft.list.page.received"; // загрузился имидж public final static String IMAGE_RECEIVED = "com.olsoft.image.received "; … }
Рассылка интентов
Отправку интентов можно сделать через методы приложения: доступно из любого класса, не требуется передавать и хранить ссылку на контекст.
public class SampleApplication extends Application { private static SampleApplication mInstance; @Override public void onCreate() { super.onCreate(); mInstance = this; … } /*** * Выдает экземпляр приложения */ public static SampleApplication getInstance() { return mInstance; } /*** * Запуск сообщения с указанным событием * * @param action – события */ public void sendBroadcastIntent(String action) { final Intent intent = new Intent(action); sendBroadcast(intent); } /*** * Сообщаем, ��то загрузилась страница списка */ public static void notifyListPageLoaded() { getInstance().sendBroadcast(new Intent(Intents. LIST_PAGE_RECEIVED)); } /*** * Сообщаем, что загрузилась картинка */ public static void notifyImageLoaded() { getInstance().sendBroadcast(new Intent(Intents.IMAGE_RECEIVED)); } … }
Поэтому рассылка интентов выглядит очень просто:
@Override public void OnDownloadComplete(byte[] data) { … parseData(data); SampleApplication.notifyListPageLoaded(); … } @Override public void OnDownloadComplete(byte[] data) { … mImage = BitmapFactory.decodeByteArray(data, 0, data.length); SampleApplication.notifyListPageLoaded(); … }
В случае необходимости в интенте можно передать дополнительные параметры, например ID картинки. Для этого используем метод putExtra.
Перехват интентов
В активити интенты перехватываем с помощью анонимного экземпляра BroadcastReceiver. Устанавливаем его в момент перехода активити на передний план и убираем, когда активити уходит с экрана.
/*** * При старте активити регистрируем обработчик событий */ @Override protected void onResume() { super.onResume(); IntentFilter f = new IntentFilter(); f.addAction(Intents.LIST_PAGE_RECEIVED); f.addAction(Intents.IMAGE_RECEIVED); registerReceiver(mNotificationReceiver, f); … } /*** * При остановке активити убираем обработчик событий */ @Override protected void onPause() { unregisterReceiver(mNotificationReceiver); super.onPause(); } /*** * Перехватчик событий. Переправляем его в обработчик для запуска в UI треде */ private BroadcastReceiver mNotificationReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (Intents.IMAGE_RECEIVED.equalsIgnoreCase(action)) { mHandler.removeMessages(MESSAGE_IMAGE_LOADED); mHandler.sendEmptyMessageDelayed(MESSAGE_IMAGE_LOADED, 250); } else if (Intents. LIST_PAGE_RECEIVED.equalsIgnoreCase(action)) { mHandler.sendEmptyMessage(MESSAGE_NEXT_PAGE); } } };
Сами события передаем в анонимный хендлер, поскольку обработка событий в активити чаще всего связано с обновлением компонентов на экране и требует исполнения в UI треде. Дополнительный плюс такого решения – можно обновлять компоненты с задержкой и отсекать часть событий, чтобы не вызывать мерцания на экране, например если каждый элемент списка содержит картинку, которая подгружается в отдельном треде (обратите внимание на методы removeMessages и sendEmptyMessageDelayed).
private final static int MESSAGE_NEXT_PAGE = 1; private final static int MESSAGE_IMAGE_LOADED = 2; /** * Обработчик сообщений. Вводим его, чтобы все события передавать в UI thread */ private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_NEXT_PAGE: mAdapter.notifyDataSetChanged(); break; case MESSAGE_IMAGE_LOADED: mAdapter.notifyDataSetChanged(); break; default: break; } super.handleMessage(msg); } };
Заключение
Приведенный здесь метод выглядит достаточно громоздким, но если учесть что в большинстве активити возможно потребуется перехват событий от тредов, то целесообразно вынести этот функционал в базовую активити и остальные наследовать от этого базового класса. Сам код внести снипетом в Eclipse и не вводить каждый раз.
