2016-03-11 3 views
1

Сегодня я прочитал несколько блогов и исходный код о том, как Handler & Looper работают вместе.Почему основной поток Looper.loop() не блокирует поток пользовательского интерфейса?

Основываясь на том, что я узнал, мы можем иметь только один Looper на каждом потоке, используя магию ThreadLocal. Обычно обработчик инициируется в основном потоке, иначе вы должны вручную запустить или сказать: prepare Looper в отдельном потоке, а затем зацикливать его.

Что меня совсем смущает loop() в главной теме. Когда я прочитал это в исходном коде Looper. Это бесконечный цикл для обработки очереди сообщений, а затем отправка сообщений для обработки обратных вызовов.

В соответствии с этим https://stackoverflow.com/a/5193981/2290191, обработчик и его Looper запускаются в той же теме.

Если на основном потоке есть бесконечный цикл, не блокирует ли он весь пользовательский интерфейс?

Я знаю, что я должен быть настолько глупым, чтобы что-то пропустить. Но было бы прекрасно, если бы кто-то раскрыл секрет этого.

public static void loop() { 
    final Looper me = myLooper(); 
    if (me == null) { 
     throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 
    } 
    final MessageQueue queue = me.mQueue; 

    // Make sure the identity of this thread is that of the local process, 
    // and keep track of what that identity token actually is. 
    Binder.clearCallingIdentity(); 
    final long ident = Binder.clearCallingIdentity(); 

    for (;;) { 
     Message msg = queue.next(); // might block 
     if (msg == null) { 
      // No message indicates that the message queue is quitting. 
      return; 
     } 

     // This must be in a local variable, in case a UI event sets the logger 
     Printer logging = me.mLogging; 
     if (logging != null) { 
      logging.println(">>>>> Dispatching to " + msg.target + " " + 
        msg.callback + ": " + msg.what); 
     } 

     msg.target.dispatchMessage(msg); 

     if (logging != null) { 
      logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); 
     } 

     // Make sure that during the course of dispatching the 
     // identity of the thread wasn't corrupted. 
     final long newIdent = Binder.clearCallingIdentity(); 
     if (ident != newIdent) { 
      Log.wtf(TAG, "Thread identity changed from 0x" 
        + Long.toHexString(ident) + " to 0x" 
        + Long.toHexString(newIdent) + " while dispatching to " 
        + msg.target.getClass().getName() + " " 
        + msg.callback + " what=" + msg.what); 
     } 

     msg.recycleUnchecked(); 
    } 
} 

ответ

2

На самом деле Looper в основном потоке позволяет рисовать. Когда представление недействительно, сообщение передается основному Looper, сообщая ему, что была запрошена ничья. Когда Looper обрабатывает это сообщение, происходит фактический чертеж. Причина, по которой другая деятельность, которая удерживает поток пользовательского интерфейса, удерживает чертеж, заключается в том, что он не позволяет Looper обрабатывать это сообщение.

Это более или менее то, как рисунок работает в любой системе на основе событий, от Windows до Mac до Android.

Почему бы не нарисовать сразу, а не отправить сообщение? Представление. Рисунок медленный. Если вы выполняете несколько изменений в ответ на событие, вы не хотите перерисовывать экран для каждого из них. Выполнение этого способа означает, что вы объединяете все свои перерисовки для обработки одного события в 1 перерисовку. Например, если вы установите текст 1 вида и изображение другого, они оба будут перерисовываться одновременно и только один раз.

+0

Не думаю, что вы внимательно прочитали мой вопрос. Я имею в виду, если существует 'for (;;) {}' loop on main thread, как можно отправлять сообщения для рисования пользовательского интерфейса? –

+0

Я сделал, я не думаю, что вы поняли мой ответ. Одно из сообщений петлителя вызывает ничью. Рисование - это специальное сообщение, отправленное на этот петлитель. –

+2

Или по-другому: что вы думаете о том, что основным потоком является то, что Looper обрабатывает сообщения и вызывает ваш код в ответ на него. Это метод, называемый циклом событий или циклом сообщений, который является общим в программировании, управляемом событиями.Вот подробное объяснение Android http://mattias.niklewski.com/2012/09/android_event_loop.html –

0

Этот вопрос является деликатной ловушкой. Почему бесконечные циклы не блокируют потоки пользовательского интерфейса, потому что все поведение потоков пользовательских интерфейсов начинается с msg.next.

Если сообщение не указано, это значит, что никаких обновлений не требуется. Весь наш код - это просто обратный вызов, например Application onCreate, Activit onCreate, BroadcastReceiver onReceive.

Все обратные вызовы обновления вызваны сообщением, и эти сообщения поступают из системных служб, таких как ActivityManagerService, InputManagerService, WindowMangerService. Если вам нужно обновить интерфейс, служба Android отправит сообщение в цикле через IPC.

Таким образом, бесконечный цикл является бесконечным обновлением.