Handler、MessageQueue、Looper,它们都是运行在UI线程上的吗?

发布于 2024-10-20 10:49:55 字数 329 浏览 2 评论 0原文

我正在尝试围绕线程进行思考,并且我知道我可以使用 Handler 将消息/可运行对象发布到 MessageQueue,而该消息又会被Looper 并发送回 Handler 进行处理。

如果我在活动中发布到 Handler ,则为 ActivityHandlerMessageQueueLooper 全部运行在 UI 线程上?如果没有,有人可以解释一下这一切是如何结合在一起的吗? :)

I'm trying to wrap my head around threading, and I know that I may use a Handler to post messages/runnables to the MessageQueue, which in turn gets picked up by the Looper and sent back to the Handler for processing.

If I post to a Handler in my activity, is the Activity, Handler, MessageQueue and Looper all running on the UI thread? If not, could someone please explain how this all comes together? :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

陈年往事 2024-10-27 10:49:55

简短回答:它们都在同一个线程上运行。如果从 Activity 生命周期回调实例化,它们都在主 UI 线程上运行。

长答案:

线程可能有一个Looper,其中包含一个MessageQueue为了使用此功能,您必须通过调用(静态)Looper.prepare() 在当前线程上创建一个Looper,然后通过调用(也是静态)<代码>Looper.loop()。它们是静态的,因为每个线程只应该有一个 Looper

loop()的调用通常在一段时间内不会返回,但会不断地从其中获取消息(“任务”、“命令”或任何您喜欢的名称) MessageQueue 并单独处理它们(例如,通过回调消息中包含的 Runnable)。当队列中没有消息时,线程会阻塞,直到有新消息为止。要停止Looper,您必须对其调用quit()(这可能不会立即停止循环,而是设置一个私有标志,定期从循环中检查该标志)循环,指示它停止)。

但是,您不能直接将消息添加到队列中。相反,您可以注册一个 MessageQueue.IdleHandler 来等待 queueIdle() 回调,您可以在其中决定是否要执行某些操作。所有处理程序都会依次调用。 (因此“队列”并不是真正的队列,而是定期调用的回调集合。)

关于上一段的注意事项: 这其实是我猜到的。我找不到任何相关文档,但这是有道理的。

更新:参见ahcox'评论他的答案

由于这是一项繁重的工作,框架提供了 Handler 类来简化事情。当您创建 Handler 实例时,它(默认情况下)绑定到已附加到当前线程的 Looper。 (Handler 知道要附加到什么 Looper,因为我们之前调用了 prepare(),它可能存储了对 Looper< 的引用。 /code> 在 ThreadLocal 中。)

使用 Handler,您只需调用 post() 即可“put一条消息进入线程的消息队列”(可以这么说)。 Handler 将处理所有 IdleHandler 回调内容,并确保您发布的 Runnable 被执行。 (如果您延迟发布,它还可能检查时间是否正确。)

需要明确的是:真正使循环线程某事的唯一方法是发布消息到它的循环。这一直有效,直到您在循环器上调用 quit() 为止。


关于 android UI 线程: 在某个时刻(可能在创建任何活动等之前),框架已经设置了一个Looper(包含一个MessageQueue)并启动它。从此时起,UI 线程上发生的所有事情都将通过该循环进行。这包括活动生命周期管理等。您重写的所有回调(onCreate()onDestroy()...)至少都是从该循环间接调度的。例如,您可以在异常的堆栈跟踪中看到这一点。 (你可以尝试一下,只需在 onCreate() 中的某处写入 int a = 1 / 0;...)


我希望这是有道理的。抱歉之前不清楚。

Short answer: they all run on the same thread. If instantiated from an Activity lifecycle callback, they all run on the main UI thread.

Long answer:

A thread may have a Looper, which contains a MessageQueue. In order to use this facility, you would have to create a Looper on the current thread by calling (the static) Looper.prepare(), and then start the loop by calling (the also static) Looper.loop(). These are static because there is only supposed to be one Looper per thread.

The call to loop() usually does not return for some time, but keeps taking messages ("tasks", "commands" or whatever you like to call them) out of the MessageQueue and handles them individually (e.g. by calling back a Runnable contained in the message). When there are no messages left in the queue, the thread blocks until there are new messages. To stop a Looper, you have to call quit() on it (which probably does not stop the loop immediately, but rather sets a private flag that is checked periodically from the loop, signaling the it to stop).

However, you cannot add messages to the queue directly. Instead, you register a MessageQueue.IdleHandler to wait for a queueIdle() callback, in which you can decide if you wish to to something or not. All handlers are called in turn. (So the "queue" isn't really a queue, but instead a collection of callbacks to be called regularly.)

Note regarding the previous paragraph: This I actually guessed. I couldn't find any documentation on that, but it would make sense.

update: see ahcox' comment and his answer.

Because this is a lot of work, the framework provides the Handler class to simplify things. When you create a Handler instance, it is (by default) bound to the Looper already attached to the current thread. (The Handler knows what Looper to attach to because we called prepare() earlier, which probably stored a reference to the Looper in a ThreadLocal.)

With a Handler, you can just call post() to "put a message into the thread's message queue" (so to speak). The Handler will take care of all the IdleHandler callback stuff and make sure your posted Runnable is executed. (It might also check if the time is right already, if you posted with a delay.)

Just to be clear: the only way to actually make a looping thread do something is to post a message to it's loop. This is valid until you call quit() on the looper.


Regarding the android UI thread: At some point (probably before any activities and the like are created) the framework has set up a Looper (containing a MessageQueue) and started it. From this point on, everything that happens on the UI thread is through that loop. This includes activity lifecycle management and so on. All callbacks you override (onCreate(), onDestroy()...) are at least indirecty dispatched from that loop. You can see that for example in the stack trace of an exception. (You can try it, just write int a = 1 / 0; somewhere in onCreate()...)


I hope this makes sense. Sorry for being unclear previously.

橘香 2024-10-27 10:49:55

跟进问题的“这一切是如何结合在一起的”部分。正如 user634618 所写,循环器接管一个线程,在情况下是主 UI 线程
应用程序的主要Looper

  • Looper.loop() 将消息从消息队列中拉出。每个消息都有一个对其要返回给(目标成员)的关联处理程序的引用。
  • 对于从队列中获取的每条消息,在 Looper.loop() 内:
    • loop() 使用存储在 Message 中作为其目标成员的 Handler 调用 public void Handler.dispatchMessage(Message msg)
    • 如果消息有 Runnable 回调成员,则运行。
    • 否则,如果处理程序设置了共享回调,则运行。
    • 否则,将使用 Message 作为参数调用 Handler 的 handleMessage()。 (请注意,如果您像 AsyncTask 那样子类化 Handler,则可以像它一样重写 handleMessage()。)

关于所有协作对象位于同一 UI 线程上的问题,
必须在与其发送的 Looper 相同的线程上创建 Handler
消息至.
它的构造函数将查找当前的 Looper 并将其存储为成员,绑定
LooperHandler
它还将直接在其自己的成员中引用该Looper的消息队列。
Handler 可用于从任何线程向 Looper 发送工作,但是这个
消息队列的标识路由要在 Looper 线程上完成的工作。

当我们在另一个线程上运行一些代码并希望发送一个 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();
}

Following up on the "how it all comes together" part of the question. As user634618 wrote, the looper takes over a thread, the main UI thread in the case of an
application's main Looper.

  • Looper.loop() pulls Messages out of its message queue. Each Message has a reference to an associated Handler that it is to be given back to (the target member).
  • Inside Looper.loop() for each message gotten from the queue:
    • loop() calls public void Handler.dispatchMessage(Message msg) using the Handler that is stored in the Message as its target member.
    • If the message has a Runnable callback member, that is run.
    • Else, if the Handler has a shared callback set, that is run.
    • Else, Handler's handleMessage() is called with the Message as an argument. (Note, if you subclass Handler as AsyncTask does, you could override handleMessage() as it does.)

On your question about all the collaborating objects being on the same UI thread,
a Handler must be created on the same thread as the Looper that it will send
messages to.
Its constructor will lookup the current Looper and store it as a member, tying
the Handler to that Looper.
It will also reference that Looper's message queue directly in its own member.
The Handler can be used to send work to the Looper from any thread, but this
identity of the message queues routes the work to be done on the Looper's thread.

When we are running some code on another thread and want to send a Runnable to be executed on the UI thread, we could do it like this:

// 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();
}
述情 2024-10-27 10:49:55

我尝试自己实现这些接口以理解这个概念。
为了简单起见,只需根据需要使用接口即可。
这是我的测试代码:

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

}

I try to implement these interface by myself in order to understand the concept.
By simplicity, just use interface by necessary.
Here is my test code:

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

}
财迷小姐 2024-10-27 10:49:55

如果我向 Activity 中的处理程序发送消息,那么 Activity、Handler、MessageQueue 和 Looper 是否都在 UI 线程上运行?如果没有,有人可以解释一下这一切是如何结合在一起的吗? :)

这取决于您如何创建 Handler

案例 1:

Handler()

默认构造函数将此处理程序与当前的 Looper 关联起来线程。

如果你在UI线程中像这样创建Handler,那么Handler就会与UI线程的Looper相关联。 MessageQueue 还与UI Thread 中的Looper 相关联。

案例2:

Handler (Looper looper)

使用提供的 Looper 而不是默认的 Looper。

如果我创建一个 HandlerThread 并将 HandlerThread 的 Looper 传递给 Handler, Handler 和 Looper 与 HandlerThread 相关,而不是与 UI Thread 相关。 HandlerMessageQueueLooperHandlerThread 相关联。

使用案例:您想要执行网络或 IO 操作。您无法在 UI 线程上执行它,因此 HandlerThread 对您来说很方便。

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

如果您想将数据从 HandlerThread 传递回 UI 线程,您可以使用 UI 线程中的 Looper 创建另一个 Handler(例如 responseHandler )并调用 sendMessage。 UI 线程 responseHandler 应覆盖 handleMessage

有关更多详细信息,请参阅这些帖子。

Looper 的用途是什么以及如何使用它?(概念)

Android:Toast一个线程(例如链接所有这些概念的代码)

If I post to a handler in my activity, is the Activity, Handler, MessageQueue and Looper all running on the UI thread? If not, could someone please explain how this all comes together? :)

It depends on how you create Handler

Case 1:

Handler()

Default constructor associates this handler with the Looper for the current thread.

If you create Handler like this in UI thread, Handler is associated with Looper of UI Thread. MessageQueue is also associated with Looper with UI Thread.

Case 2:

Handler (Looper looper)

Use the provided Looper instead of the default one.

If I create a HandlerThread and pass the Looper of HandlerThread to Handler, Handler and Looper are associated with HandlerThread and not UI Thread. Handler, MessageQueue and Looper are associated with HandlerThread.

Use case: You want to execute a Network OR IO operation. You can't execute it on UI Thread and hence HandlerThread is handy for you.

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

If you want to pass data back from HandlerThread to UI Thread, you can create one more Handler ( say responseHandler ) with Looper from UI Thread and call sendMessage. The UI Thread responseHandler should override handleMessage

Refer to these posts for more details.

What is the purpose of Looper and how to use it? ( For concepts )

Android: Toast in a thread ( For example code by linking all these concepts)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文