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

所以,正如标题所说:当你将另一个线程的runnable发布到主线程上时会发生什么?

我已经看到很多问题,询问你是如何做到这一点的,以及它的基础知识是如何运作的。 但是我很难找到一个确切的解释,当你在MessageQueue上放置一个runnable时实际发生了什么。 当它在Runnable的转弯时运行,当然。 但这是什么时候?

例如:
假设有一个启动ASync请求的按钮,请求返回并触发在MainThread上运行的runnable / callback。 怎么了? runnable被添加到MessageQueue并在’time’时运行。 但是什么时候到了? 如果我在Async请求发布MainThread上的runnable之前按下另一个在MainThread上执行半长阻塞任务的按钮怎么办? 是否等到我的阻止按钮上的逻辑完成? 它会打断它吗? 它是否将可运行代码与我的阻塞代码按钮的代码交织在一起? 究竟发生了什么?

我问的主要原因是,我可以更好地理解我需要考虑哪些因素来防止multithreading引起的错误。 (特别是影响已刷新页面的旧请求的情况)

首先,您需要了解Message类是什么样的。 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 

我用[RUNNABLE][SWITCHED]标记的内容表示处理Message的两种非重叠方式。 如果callback不为null,则忽略所有[SWITCHED]字段。 如果callback为空,而不是由[SWITCHED]字段定义的Message ,并且在Handler's覆盖handleMessage()handleMessage()中处理,则处理程序初始化为。

MessageQueuewhen字段排序。 Looper不会出列消息并处理消息,直到当前时间(由SystemClock.uptimeMillis测量)大于或等于消息的when字段中存储的时间。

当您调用Handler#post(Runnable r) ,会发生以下情况:

  1. 从池中获取MessageMessage类中的简单静态链接列表)

  2. 您的Runnable被分配给消息的callback字段。

  3. 如果没有传递延迟或特定时间,则将字段简单设置为当前时间

  4. Message被排入MessageQueue 。 如果when早于队列头部的那个,它就变成了一个新头。 如果不是,则将其插入中间,以便MessageQueuewhen排序

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

  6. 处理程序决定消息是[RUNNABLE]还是[SWITCHED]并相应地处理它。 特别是如果它存在的话,它会在callback上调用run()

这应该回答你关于在阻塞任务期间发布在UI线程上的Runnable的行为的问题 – 好吧, 不,它不会中断正在进行的任务,也不会交织 。 线程上发生的一切都会首先进入MessageQueue ,按钮单击或您从其他线程发布的自定义Runnables 。 基本上没有办法以其他方式发生: Looper.loop()只是让线程忙于for(;;)循环。

有一些方法可以改变消息的顺序。

例如,Looper / Handler框架中有一个有趣的同步屏障概念。 同步障碍是一种惯例,只是一个带有空targetMessage (因此它基本上只是一个类似于旗帜的东西,没有处理程序来调度它)。 如果使用postSyncBarrier()将其放入队列,则整个出队的过程会发生变化,直到使用removeSyncBarrier()从队列中删除同步障碍。 未标记为isAsynchronousMessages将被忽略,并且根本不会出列和处理。 而是扫描队列,直到找到isAsynchronous = true的消息。 然后根据它的时间进行安排,并在时机到来when进行处理。

另外,你可以调用一个不言自明的Handler#postAtFrontOfQueue() ,正如文档中所指出的那样

此方法仅用于非常特殊的情况 – 它可能很容易使消息队列饿死,导致排序问题或具有其他意外的副作用。

我建议你浏览所有提到的类的源代码 。 它读起来就像一本好书。

MainThread执行了许多其他runnable,例如更新UI,触摸事件。 ‘时间’是指已发布的runnable准备好出列的时间。 如果任何其他runnable出现在它之前,你的runnable将等待。

这里没有中断这样的事情。 你的按钮将提交一系列runnables,就像从许多不同的线程提交相同数量的runnable一样。

如果您有一条非短消息(任何包含LONG字对UI不利的话)操作将阻止执行队列中提交的其他重复任务,大多数情况下通常都会显示没有更新(对于执行不确定的任务)如果是执行需要超过8毫秒的可运行的突发问题,那么根本就是UI。