Обновление 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 можно также использовать повторно, сохраняя его как поле.