Подтвердить что ты не робот

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

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

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

4b9b3361

Ответ 1

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

Длинный ответ:

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

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

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

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

update: см. комментарий ahcox и его ответ. p >

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

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

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


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


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

Ответ 2

Взаимосвязь с вопросом "как все это вместе". Как писал user634618, петлератор берет поток, основной поток пользовательского интерфейса в случае application main Looper.

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

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

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

// 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();
}

Ответ 3

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

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();
    }

}

Ответ 4

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

Это зависит от того, как вы создаете Handler

Случай 1:

Handler()

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

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

Случай 2:

Handler (Looper looper)

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

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

Случай использования: вы хотите выполнить операцию Network or IO. Вы не можете выполнить его в потоке пользовательского интерфейса, и поэтому HandlerThread вам подходит.

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

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

Подробнее см. в этих сообщениях.

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

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