Обработчики, MessageQueue, Looper, все ли они работают в потоке пользовательского интерфейса?

Я пытаюсь обернуть голову вокруг резьбы, и я знаю, что могу использовать Handler для отправки сообщений / runnables в MessageQueue, который, в свою очередь, подхватывается Looper и отправлено обратно обработчику для обработки.

если я отправляю сообщение обработчику в своей деятельности, это Activity, Handler, MessageQueue и Looper все работает в потоке пользовательского интерфейса? Если нет, может кто-нибудь объяснить, как все это происходит? :)

4 ответов


короткий ответ: все они работают в одном потоке. Если экземпляр создан из Activity обратный вызов жизненного цикла, все они работают в основном потоке пользовательского интерфейса.

ответ:

нить может иметь Looper, которая содержит MessageQueue. чтобы использовать этот объект, вам нужно будет создать Looper в текущем потоке, вызвав (статический)Looper.prepare(), а затем запустите цикл, вызвав (также статический)Looper.loop(). Они статичны, потому что должен быть только один Looper в каждом потоке.

вызов loop() обычно не возвращает в течение некоторого времени, но продолжает принимать сообщения ("задачи"," команды " или как вы их называете) из MessageQueue и обрабатывает их по отдельности (например, вызывая a Runnable содержится в сообщении). Если в очереди не осталось сообщений, поток блокируется до появления новых сообщений. Чтобы остановить Looper, вы должны позвонить quit() на нем (который, вероятно, не останавливает цикл сразу, а скорее устанавливает частный флаг, который периодически проверяется из цикла, сигнализируя ему остановиться).

однако вы не можете добавлять сообщения в очередь напрямую. Вместо этого, вы регистрируете MessageQueue.IdleHandler ждать queueIdle() обратный вызов, в котором вы можете решить, хотите ли вы что-то или нет. Все обработчики вызываются по очереди. (Так "очередь" на самом деле не очередь, а вместо этого коллекция обратные вызовы должны вызываться регулярно.)

Примечание относительно предыдущего абзаца: это я на самом деле догадался. Я не мог найти никакой документации по этому поводу, но это имело бы смысл.

обновление: посмотреть ahcox комментарий и ответ.

поскольку это много работы, фреймворк предоставляет Handler класс, чтобы упростить вещи!--43-->. При создании Handler экземпляр, он (по умолчанию) привязан к Looper уже подключен к текущему потоку. (The Handler знает, что Looper для присоединения, потому что мы вызвали prepare() ранее, который, вероятно, сохранил ссылку на Looper на ThreadLocal.)

С Handler, вы можете просто позвонить post() "поставить сообщение в очередь сообщений потока" (так сказать). The Handler позаботится обо всех IdleHandler обратный звонок вещи и убедитесь, что ваш комментария Runnable выполняется. (Он также может проверить, правильно ли время уже, если вы отправили с задержкой.)

просто чтобы быть ясным: единственный способ на самом деле сделать петлевой поток do что-то должно отправить сообщение в его цикл. Это действительно до тех пор, пока вы не вызовете quit() на looper.


относительно потока пользовательского интерфейса android: в какой-то момент (возможно, перед любыми действиями и тому подобное создано) платформа настроила Looper (содержащей MessageQueue) и запустил его. С этого момента все, что происходит в потоке пользовательского интерфейса через эту петлю. Это включает управление жизненным циклом деятельности и так далее. Все обратные вызовы, которые вы переопределяете (onCreate(), onDestroy()...) по крайней мере indirecty отправляется из этого цикла. Это можно увидеть, например, в трассировке стека исключения. (Вы можете попробовать, просто напишите int a = 1 / 0; где-то в onCreate()...)


я надеюсь, что это обретать смысл. Извините за то, что был неясен ранее.


Продолжение части вопроса "как все это происходит". Как писал user634618, looper берет на себя поток, основной поток пользовательского интерфейса в случае главное приложение Looper.

  • Looper.loop() извлекает сообщения из очереди сообщений. Каждое сообщение имеет ссылку на связанный обработчик, которому оно должно быть возвращено (целевой элемент).
  • внутри Looper.loop() для каждого сообщения из очереди:
    • loop() вызовы public void Handler.dispatchMessage(Message msg) используя обработчик, который хранится в сообщении в качестве его целевого члена.
    • если сообщение имеет выполняемый элемент обратного вызова, это выполняется.
    • еще, если обработчик имеет общий набор обратного вызова, который выполняется.
    • еще, обработчик handleMessage() вызывается с сообщением в качестве аргумента. (Обратите внимание, если вы обработчик подкласса, как AsyncTask, вы можете переопределить handleMessage() как он это делает.)

на ваш вопрос о все сотрудничающие объекты, находящиеся в одном потоке UI, а Handler должен быть создан в том же потоке, как Looper что он пошлет сообщения. Его конструктор будет искать тег Looper и сохраните его как член, связывая the Handler этой Looper. Она также будет ссылаться на то, что Looperочередь сообщений непосредственно в собственном члене. The Handler можно использовать для отправки работы в Looper из любого потока, но это идентификатор очереди сообщений, направляет работу на Looper ' s нитка.

когда мы запускаем некоторый код в другом потоке и хотим отправить Runnable для выполнения в потоке UI, мы можем сделать это следующим образом:

// h is a Handler that we constructed on the UI thread.
public void run_on_ui_thread(final Handler h, final Runnable r)
{
   // Associate a Message with our Handler and set the Message's
   // callback member to our Runnable:
   final Message message = Message.obtain(h, r);

   // The target is the Handler, so this asks our Handler to put
   // the Message in its message queue, which is the exact same
   // message queue associated with the Looper on the thread on
   // which the Handler was created:
   message.sendToTarget();
}

Я пытаюсь реализовать этот интерфейс самостоятельно, чтобы понять концепцию. По простоте, Просто используйте интерфейс по необходимости. Вот мой тестовый код:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class TestLooper {

    public static void main(String[] args) {
        UIThread thread = new UIThread();
        thread.start();

        Handler mHandler = new Handler(thread.looper);
        new WorkThread(mHandler, "out thread").run();
    }
}

class Looper {
    private BlockingQueue<Message> message_list = new LinkedBlockingQueue<Message>();

    public void loop() {

        try {
            while (!Thread.interrupted()) {
                Message m = message_list.take();
                m.exeute();
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public void insertMessage(Message msg) {
        message_list.add(msg);
    }

}

class Message {
    String data;
    Handler handler;

    public Message(Handler handler) {
        this.handler = handler;
    }

    public void setData(String data) {
        this.data = data;
    }

    public void exeute() {
        handler.handleMessage(this);
    }
}

class Handler {

    Looper looper;

    public Handler(Looper looper) {
        this.looper = looper;
    }

    public void dispatchMessage(Message msg) {
        System.out.println("Handler dispatchMessage" + Thread.currentThread());
        looper.insertMessage(msg);
    }

    public Message obtainMessage() {
        return new Message(this);
    }

    public void handleMessage(Message m) {
        System.out.println("handleMessage:" + m.data + Thread.currentThread());
    }
}

class WorkThread extends Thread {
    Handler handler;
    String tag;

    public WorkThread(Handler handler, String tag) {
        this.handler = handler;
        this.tag = tag;
    }

    public void run() {
        System.out.println("WorkThread run" + Thread.currentThread());
        Message m = handler.obtainMessage();
        m.setData("message " + tag);
        handler.dispatchMessage(m);
    }
}

class UIThread extends Thread {

    public Looper looper = new Looper();

    public void run() {

            //create handler in ui thread
        Handler mHandler = new Handler(looper);

        new WorkThread(mHandler, "inter thread").run();
        System.out.println("thead run" + Thread.currentThread());
        looper.loop();
    }

}

если я отправляю сообщение обработчику в моей деятельности, выполняется ли Activity, Handler, MessageQueue и Looper в потоке пользовательского интерфейса? Если нет, может кто-нибудь объяснить, как все это происходит? :)

это зависит от того, как вы создадите проводник

Пример 1:

Handler()

конструктор по умолчанию связывает этот обработчик с Looper для текущего потока.

если вы создаете Handler как это в потоке пользовательского интерфейса, Handler связан с Looper потока пользовательского интерфейса. MessageQueue также связано с Looper С потоком пользовательского интерфейса.

Пример 2:

Handler (Looper looper)

используйте предоставленный петлитель вместо стандартного.

если я создам HandlerThread и передать петлителя в HandlerThread в обработчик, обработчик и петлитель связаны с HandlerThread и не в потоке UI. Handler, MessageQueue и Looper связано с HandlerThread.

вариант использования: вы хотите выполнить операцию сети или ввода-вывода. Вы не можете выполнить его в потоке UI и, следовательно,HandlerThread очень удобно для вас.

 HandlerThread handlerThread = new HandlerThread("NetworkOperation");
 handlerThread.start();
 Handler requestHandler = new Handler(handlerThread.getLooper());

если вы хотите передать данные из HandlerThread в поток пользовательского интерфейса, вы можете создать еще один обработчик (скажем, responseHandler ) с Looper из потока пользовательского интерфейса и вызова sendMessage. Поток пользовательского интерфейса responseHandler должны переопределить handleMessage

см. эти сообщения для получения дополнительной информации подробности.

какова цель Looper и как его использовать? ( по понятиям )

Android: тост в потоке (например, код, связывая все эти понятия)