В этом топике рассматривается еще один способ синхронизации потоков с использованием 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 и не вводить каждый раз.