Android:将可运行对象从另一个线程发布到主线程上实际上有什么作用? [英] Android: What does posting a runnable from another thread onto the main thread actually do?
问题描述
那么,正如标题所述:当您将可运行对象从另一个线程发布到主线程时,实际上会发生什么?
So, as the title says: What actually happens when you post a runnable from another thread onto the main thread?
我已经看到很多问题,询问您如何进行以及其基本原理.但是,当您将可运行对象放在MessageQueue上时,我很难找到确切的解释.当然,它在轮到Runnable时运行.但是,这是什么时候?
I've seen lots of questions asking how you do it, and how the basics of it work. But I've had a hard time finding an exact explanation of what actually happens when you put a runnable on the MessageQueue. It runs when it is the Runnable's turn, sure. But when is this?
例如:
假设有一个按钮启动ASync请求,并且该请求返回并触发在MainThread上运行的可运行/回调.怎么了?可运行对象被添加到MessageQueue中,并在时间"开始运行.但是什么时候是时间"?如果我在Async请求在MainThread上发布可运行对象之前按下另一个在MainThread上执行一些半长阻塞任务的按钮,该怎么办?它会一直等到阻止按钮上的逻辑完成吗?它会打断吗?它将可运行代码与我的阻止代码按钮的代码交织在一起吗?到底会发生什么?
So for example:
Assuming that there's a button that launches an ASync request, and the request returns and triggers a runnable/callback that runs on the MainThread. What happens? The runnable gets added to a MessageQueue and runs when it is 'time'. But when is it 'time'? What if I press another button that does some semi-long blocking task on the MainThread just before the Async request posts the runnable on MainThread? Does it wait until the logic on my blocking button is completed? Does it interrupt it? Does it interweave the runnable code with the code of my blocking-code button? What happens exactly?
我要问的主要原因是,这样我可以更好地了解我需要牢记的注意事项,以防止由于多线程导致的错误. (特别是旧请求会影响已刷新的页面)
Main reason I'm asking is so I can get a better understanding of what considerations I need to keep in mind to prevent errors due to multithreading. (Specifically the case of old requests affecting pages that have already been refreshed)
推荐答案
首先,您需要了解Message
类的外观. Message
对象包含以下其他字段:
First, you need to understand what the Message
class is like. A Message
object contains among other fields the following:
Handler target; // a handler that enqueued the message
long when; // the time at which the message is to be processed
[RUNNABLE] Runnable callback; =
[SWITCHED] int what, int arg1, int arg2, Bundle data...
bool isAsynchronous; // I will talk about it in the end
我用 [RUNNABLE] 和 [SWITCHED] 标记的内容表示处理Message
的两种不重叠的方式.如果callback
不为null,则所有 [SWITCHED] 字段都将被忽略.如果callback
为空,则Message
由 [SWITCHED] 字段定义,并在Handler's
覆盖的handleMessage()
或Handler.Callback
的handleMessage()
中处理.处理程序已初始化.
What I tagged with [RUNNABLE] and [SWITCHED] represents the two non-overlapping means of processing a Message
. If the callback
is not null all the [SWITCHED] fields are ignored. If the callback
is null than the Message
is defined by [SWITCHED] fields and is processed in either the Handler's
overriden handleMessage()
or the handleMessage()
of the Handler.Callback
the handler was initialized with.
MessageQueue
按when
字段排序.在SystemClock.uptimeMillis
度量的当前时间大于或等于消息的when
字段中存储的时间之前,Looper
不会使消息出队并处理消息.
The MessageQueue
is sorted by the when
field. The Looper
will not dequeue and process a message until the current time, as measured by SystemClock.uptimeMillis
, is greater than or equal to the time stored in the message’s when
field.
当您致电Handler#post(Runnable r)
时,会发生以下情况:
When you call Handler#post(Runnable r)
the following things happen:
-
从池中获得
-
A
Message
(一个简单的静态链表 在Message
类中)
A
Message
is obtained from the pool (a simple static linked list in theMessage
class)
您的Runnable
被分配给邮件的callback
字段.
Your Runnable
is assigned to the message's callback
field.
when
字段仅设置为当前时间
时间过去了
when
field is simply set to the current time if no delay or specific
time was passed
Message
排队到MessageQueue
.如果when
是
比队列的开头更早,它变成了新的开头.如果
不是,而是插入到中间,这样MessageQueue
依when
The Message
is enqueued to the MessageQueue
. If when
is
earlier than that of the head of the queue it becomes a new head. If
it's not, than it's inserted in the middle so that the MessageQueue
remains sorted by when
处于非终止循环中的Looper
使消息出队
从队列开始并按顺序处理它们(不交织),
最终,使我们的消息出队并在dispatchMessage()
上调用
最初发布Runnable
的处理程序.
The Looper
which was in a non-terminating loop dequeuing the messages
from the queue and processing them in sequence (no interweaving),
eventually, dequeues our message and calls dispatchMessage()
on
the handler that originally posted the Runnable
.
处理程序决定消息是 [RUNNABLE] 还是
[SWITCHED] 并进行相应处理.特别是它呼吁
callback
上的run()
(如果存在)
The handler decides whether the message is [RUNNABLE] or
[SWITCHED] and processes it accordingly. In particular it calls
run()
on the callback
if it's present
这应该回答您在阻塞任务期间在UI线程上发布的Runnable
行为的问题-很好,不,它不会中断正在进行的任务,也不会交织.线程上发生的所有事情首先进入MessageQueue
,单击按钮或从其他线程发布的自定义Runnables
.基本上不可能发生其他事情:Looper.loop()
只是使线程忙于它的for(;;)
循环.
This should answer your questions on the behavior of your Runnable
posted on the UI Thread during the blocking task - well, no, it does not interrupt the ongoing task, nor does it interweave. Everything that happens on the thread first gets into the MessageQueue
, button clicks or your custom Runnables
that you post from other threads. There is basically no way it could be happening some other way: Looper.loop()
just makes the thread busy with its for(;;)
loop.
尽管有多种方法可以更改邮件顺序.
例如,在Looper/Handler框架中有一个有趣的同步屏障概念.按照惯例,同步屏障只是一个Message
和一个空的target
(因此,它基本上只是一个类似标志的东西,没有处理程序可以分派它).如果将它与postSyncBarrier()
放在队列中,则出队的整个过程都会更改,直到使用removeSyncBarrier()
从队列中删除同步屏障.未标记为isAsynchronous
的Messages
将被忽略,并且不会完全出队和处理.而是,将扫描队列,直到找到带有isAsynchronous = true
的消息.然后将根据其when
对其进行调度,并在时间到来时对其进行处理.
For instance, there is an interesting concept of sync-barrier in the Looper/Handler framework. A sync-barrier is by a convention just a Message
with a null target
(so it's basically just a flag-like thing, there is no handler to dispatch it). If it's put to the queue with postSyncBarrier()
, the whole process of dequeuing changes, until the sync-barrier is removed from the queue with removeSyncBarrier()
. The Messages
not marked as isAsynchronous
will be ignored and not dequeued and processed at all. Instead, the queue will be scanned until the message with isAsynchronous = true
is found. It will then be scheduled according to its when
and processed when its time comes.
不过,正如文档中指出的那样,您也可以称呼不言自明的Handler#postAtFrontOfQueue()
Also, you can call a self-explanatory Handler#postAtFrontOfQueue()
, though, as pointed out in the documentation
此方法仅在非常特殊的情况下使用-它可以 容易饿死消息队列,导致订购问题或 其他意外的副作用.
This method is only for use in very special circumstances -- it can easily starve the message queue, cause ordering problems, or have other unexpected side-effects.
I suggest you browse the source code of all the classes mentioned. It reads like a good fiction book.
这篇关于Android:将可运行对象从另一个线程发布到主线程上实际上有什么作用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!