简体   繁体   English

Android:从另一个线程向主线程发布runnable实际上做了什么?

[英]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? 所以,正如标题所说:当你将另一个线程的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. 但是,当你在MessageQueue上放置一个runnable时,我很难找到确切的解释。 It runs when it is the Runnable's turn, sure. 肯定是它在Runnable转弯时运行。 But when is this? 但这是什么时候?

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. 假设有一个启动ASync请求的按钮,请求返回并触发在MainThread上运行的runnable / callback。 What happens? 怎么了? The runnable gets added to a MessageQueue and runs when it is 'time'. runnable被添加到MessageQueue并在'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? 如果我在Async请求发布MainThread上的runnable之前按下另一个在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) (特别是影响已刷新页面的旧请求的情况)

First, you need to understand what the Message class is like. 首先,您需要了解Message类是什么样的。 A Message object contains among other fields the following: Message对象包含以下字段:

    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

What I tagged with [RUNNABLE] and [SWITCHED] represents the two non-overlapping means of processing a Message . 我用[RUNNABLE][SWITCHED]标记的内容表示处理Message的两种非重叠方式。 If the callback is not null all the [SWITCHED] fields are ignored. 如果callback不为null,则忽略所有[SWITCHED]字段。 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. 如果callback为空比Message[SWITCHED]字段和无论是在被处理Handler's被覆盖的handleMessage()handleMessage()的中Handler.Callback处理程序用初始化。

The MessageQueue is sorted by the when field. MessageQueuewhen字段排序。 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. Looper不会出列消息并处理消息,直到当前时间(由SystemClock.uptimeMillis测量)大于或等于消息的when字段中存储的时间。

When you call Handler#post(Runnable r) the following things happen: 当您调用Handler#post(Runnable r) ,会发生以下情况:

  1. A Message is obtained from the pool (a simple static linked list in the Message class) 从池中获取MessageMessage类中的简单静态链接列表)

  2. Your Runnable is assigned to the message's callback field. 您的Runnable被分配给消息的callback字段。

  3. when field is simply set to the current time if no delay or specific time was passed when字段被简单地设置为当前时间,如果没有延迟或特定时间传递

  4. The Message is enqueued to the MessageQueue . Message被排入MessageQueue If when is earlier than that of the head of the queue it becomes a new head. 如果when早于队列头部的那个,它就变成了一个新头。 If it's not, than it's inserted in the middle so that the MessageQueue remains sorted by when 如果不是,则将其插入中间,以便MessageQueuewhen排序

  5. 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 . Looper处于非终止循环中,从队列中出列消息并按顺序处理它们(没有交织),最终使我们的消息出列,并在最初发布Runnable的处理程序上调用dispatchMessage()

  6. The handler decides whether the message is [RUNNABLE] or [SWITCHED] and processes it accordingly. 处理程序决定消息是[RUNNABLE]还是[SWITCHED]并相应地处理它。 In particular it calls run() on the callback if it's present 特别是如果它存在的话,它会在callback上调用run()

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 . 这应该回答你关于在阻塞任务期间发布在UI线程上的Runnable的行为的问题 - 好吧, 不,它不会中断正在进行的任务,也不会交织 Everything that happens on the thread first gets into the MessageQueue , button clicks or your custom Runnables that you post from other threads. 线程上发生的所有事情首先进入MessageQueue ,按钮单击或您从其他线程发布的自定义Runnables There is basically no way it could be happening some other way: Looper.loop() just makes the thread busy with its for(;;) loop. 基本上没有办法以其他方式发生: Looper.loop()只是让线程忙于for(;;)循环。

There are ways to change the messages ordering though. 有一些方法可以改变消息的顺序。

For instance, there is an interesting concept of sync-barrier in the Looper/Handler framework. 例如,Looper / Handler框架中有一个有趣的同步屏障概念。 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). 同步障碍是一种惯例,只是一个带有空targetMessage (所以它基本上只是一个类似于旗帜的东西,没有处理程序来调度它)。 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() . 如果使用postSyncBarrier()将其放入队列,则整个队列的更改将更改,直到使用removeSyncBarrier()从队列中删除同步屏障。 The Messages not marked as isAsynchronous will be ignored and not dequeued and processed at all. 未标记为isAsynchronousMessages将被忽略,并且根本不会出列和处理。 Instead, the queue will be scanned until the message with isAsynchronous = true is found. 而是扫描队列,直到找到isAsynchronous = true的消息。 It will then be scheduled according to its when and processed when its time comes. 然后根据它的时间进行安排,并在时机到来when进行处理。

Also, you can call a self-explanatory Handler#postAtFrontOfQueue() , though, as pointed out in the documentation 另外,你可以调用一个不言自明的Handler#postAtFrontOfQueue() ,如文档中所指出的那样

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. 它读起来就像一本好书。

There are bunch of other runnables that the MainThread executes, such as updating the UI, touch events. MainThread执行了许多其他runnable,例如更新UI,触摸事件。 The 'time' is when the posted runnable is ready to be dequeued. '时间'是发布的runnable准备好出列的时间。 If any other runnable came before it, your runnable will wait. 如果任何其他runnable出现在它之前,你的runnable将等待。

There is no such thing here as interruption. 这里没有中断这样的事情。 Your button will submit burst of runnables, as same as submitting same number of runnables from many different threads. 你的按钮将提交一系列runnables,就像从许多不同的线程提交相同数量的runnable一样。

If you have a message that is non-short (whatever contains LONG word is bad for UI) operation will block the execution of other recurrent tasks submitted in the queue, most of often demonstrated with no updates (for task that execution is indeterminate) on the UI at all or junking if it is matter of burst of runnables that execution takes longer than 8ms. 如果你有一条非短消息(任何包含LONG字对UI不好的话)操作将阻止执行队列中提交的其他重复任务,大多数情况下经常会证明没有更新(对于执行不确定的任务)如果是执行需要超过8毫秒的可运行的突发问题,那么根本就是用户界面。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM