Handler、MessageQueue、Looper,它们都是运行在UI线程上的吗?
我正在尝试围绕线程进行思考,并且我知道我可以使用 Handler
将消息/可运行对象发布到 MessageQueue
,而该消息又会被Looper
并发送回 Handler
进行处理。
如果我在活动中发布到 Handler
,则为 Activity
、Handler
、MessageQueue
和 Looper
全部运行在 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
简短回答:它们都在同一个线程上运行。如果从
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 aMessageQueue
. In order to use this facility, you would have to create aLooper
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 oneLooper
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 theMessageQueue
and handles them individually (e.g. by calling back aRunnable
contained in the message). When there are no messages left in the queue, the thread blocks until there are new messages. To stop aLooper
, you have to callquit()
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 aMessageQueue.IdleHandler
to wait for aqueueIdle()
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 aHandler
instance, it is (by default) bound to theLooper
already attached to the current thread. (TheHandler
knows whatLooper
to attach to because we calledprepare()
earlier, which probably stored a reference to theLooper
in aThreadLocal
.)With a
Handler
, you can just callpost()
to "put a message into the thread's message queue" (so to speak). TheHandler
will take care of all theIdleHandler
callback stuff and make sure your postedRunnable
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 aMessageQueue
) 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 writeint a = 1 / 0;
somewhere inonCreate()
...)I hope this makes sense. Sorry for being unclear previously.
跟进问题的“这一切是如何结合在一起的”部分。正如 user634618 所写,循环器接管一个线程,在情况下是主 UI 线程
应用程序的主要
Looper
。Looper.loop()
将消息从消息队列中拉出。每个消息都有一个对其要返回给(目标成员)的关联处理程序的引用。Looper.loop()
内:loop()
使用存储在 Message 中作为其目标成员的 Handler 调用public void Handler.dispatchMessage(Message msg)
。handleMessage()
。 (请注意,如果您像 AsyncTask 那样子类化 Handler,则可以像它一样重写handleMessage()
。)关于所有协作对象位于同一 UI 线程上的问题,
必须在与其发送的 Looper 相同的线程上创建
Handler
消息至.
它的构造函数将查找当前的 Looper 并将其存储为成员,绑定
该
Looper
的Handler
。它还将直接在其自己的成员中引用该
Looper
的消息队列。Handler
可用于从任何线程向Looper
发送工作,但是这个消息队列的标识路由要在 Looper 线程上完成的工作。
当我们在另一个线程上运行一些代码并希望发送一个 Runnable 在 UI 线程上执行时,我们可以这样做:
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).Looper.loop()
for each message gotten from the queue:loop()
callspublic void Handler.dispatchMessage(Message msg)
using the Handler that is stored in the Message as its target member.handleMessage()
is called with the Message as an argument. (Note, if you subclass Handler as AsyncTask does, you could overridehandleMessage()
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 theLooper
that it will sendmessages to.
Its constructor will lookup the current
Looper
and store it as a member, tyingthe
Handler
to thatLooper
.It will also reference that
Looper
's message queue directly in its own member.The
Handler
can be used to send work to theLooper
from any thread, but thisidentity 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:
我尝试自己实现这些接口以理解这个概念。
为了简单起见,只需根据需要使用接口即可。
这是我的测试代码:
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:
这取决于您如何创建 Handler
案例 1:
如果你在UI线程中像这样创建
Handler
,那么Handler
就会与UI线程的Looper
相关联。MessageQueue
还与UI Thread 中的Looper
相关联。案例2:
如果我创建一个 HandlerThread 并将 HandlerThread 的 Looper 传递给 Handler, Handler 和 Looper 与 HandlerThread 相关,而不是与 UI Thread 相关。
Handler
、MessageQueue
和Looper
与HandlerThread
相关联。使用案例:您想要执行网络或 IO 操作。您无法在 UI 线程上执行它,因此 HandlerThread 对您来说很方便。
如果您想将数据从 HandlerThread 传递回 UI 线程,您可以使用 UI 线程中的
Looper
创建另一个 Handler(例如 responseHandler )并调用sendMessage
。 UI 线程responseHandler
应覆盖handleMessage
有关更多详细信息,请参阅这些帖子。
Looper 的用途是什么以及如何使用它?(概念)
Android:Toast一个线程(例如链接所有这些概念的代码)
It depends on how you create Handler
Case 1:
If you create
Handler
like this in UI thread,Handler
is associated withLooper
of UI Thread.MessageQueue
is also associated withLooper
with UI Thread.Case 2:
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
andLooper
are associated withHandlerThread
.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.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 callsendMessage
. The UI ThreadresponseHandler
should overridehandleMessage
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)