Обновление GUI: Runnables vs Messages
Чтобы обновить GUI из других потоков, в основном существуют два основных подхода:
-
Используйте java.lang.Runnable с помощью любого из этих методов:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
Handler.post(Runnable)
-
Использование android.os.Message:
Handler.sendMessage(Message) / Handler.handleMessage(Message)
Вы также можете использовать AsyncTask, но мой вопрос более сфокусирован на прецеденте обновления очень простого компонента. Посмотрим, как это будет сделано с использованием обоих подходов:
-
Использование Runnables:
TextViev tv = ...;
final String data = "hello";
Runnable r = new Runnable() {
@Override
public void run(){
tv.setText(data);
}
};
//Now call Activity.runOnUiThread(r) or handler.post(r), ...
-
Использование сообщений:
Message m = handler.obtainMessage(UPDATE_TEXT_VIEW, "hello");
handler.sendMessage(m);
//Now on handler implementation:
@Override
public void handleMessage(Message msg) {
if(msg.what == UPDATE_TEXT_VIEW){
String s = (String) msg.obj;
tv.setText(data);
} ... //other IFs?
}
IMHO, сообщения не подходят, потому что:
- Нелегко понять для новых программистов, не относящихся к андроиду (обработчик привязывается к своей нити во время построения).
- Полезная нагрузка объекта должна быть Parcellable, если сообщение пересекает границы процесса.
- Сообщения повторно используются (ошибка подвержена неправильной очистке?)
- Обработчик имеет двойную роль (он отправляет сообщения, но также обрабатывает их)
- Атрибуты сообщений общедоступны, но также предлагают getter/setter.
С другой стороны, Runnables следуют хорошо известному шаблону команды и более удобны в программировании и читабельны.
Итак, каковы преимущества использования сообщений над Runnables? Сообщения, введенные в фоновом режиме в современных Android-программах? Есть ли что-нибудь, что вы можете сделать с Сообщениями, которые нельзя сделать с помощью Runnables?
Спасибо заранее.
Ответы
Ответ 1
Я бы сказал, что существует небольшая разница между использованием Message
vs a Runnable
. Это в основном сводится к личным предпочтениям. Зачем? Посмотрев исходный код, вы обнаружите, что размещение Runnable
использует тот же самый механизм обмена сообщениями. Он просто привязывает Runnable
к Message
и отправляет это.
4.4.2 Исходный код
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
Ссылка: Grep Code - Handler
Ответ 2
Messages
может быть повторно использован, поэтому он приводит к меньшему количеству созданных объектов и меньше GC. Вы также получаете меньше классов и анонимных типов.
Одно из преимуществ заключается в том, что класс, отправляющий Message
в Handler
, не должен знать ничего о реализации этого Message
. Это может помочь в инкапсуляции в зависимости от того, где она используется.
Наконец, рассмотрим разницу в чистоте между
mHandler.obtainMessage(DO_STUFF, foo).sendToTarget();
против
final Foo tempFoo = foo;
mHandler.post(new Runnable(){
@Override
public void run(){
doStuff(tempFoo);
}
};
Если у вас есть несколько мест, где вам нужно будет doStuff()
, первое будет более читаемым, и у вас будет меньше дублирования кода.
Ответ 3
Handler
интерфейс обеспечивает гораздо большую функциональность, чем runOnUiThread()
, согласно документам:
Для обработчика используются два основных способа:
(1) для планирования сообщений и исполняемых файлов, которые будут выполняться как часть будущего | (2), чтобы установить действие, которое должно выполняться в другом потоке, чем ваш собственный.
runOnUiThread
выполняет только подмножество (2). т.е. "поставить в очередь действие, которое должно выполняться в потоке пользовательского интерфейса"
Итак, IMO, если вам не нужны эти дополнительные функции runOnUiThread
, является достаточным и предпочтительным способом.
Ответ 4
Я предпочитаю от Runnable
до Message
. Я думаю, что код с использованием Runnable
намного ясен, чем Message
, потому что код обработки событий очень близок к событию. Кроме того, вы можете избежать накладных расходов на определение констант и случаев переключения.
И я не думаю, что использование Runnable
нарушает инкапсуляцию. Вы можете извлечь код в Runnable.run()
в другой метод во внешнем классе, например on...Event()
, или даже перенести его в объект EventHandler
. Оба способа намного яснее, чем использование Message
, особенно если вам нужны ссылки на хранилище в Message
, поскольку использование Runnable
позволяет избежать downcasting msg.obj
. И безымянное поле msg.obj
также подвержено ошибкам и иногда неэффективно для понимания.
И Runnable
можно также использовать повторно, сохраняя его как поле.