Android:将可运行对象从另一个线程发布到主线程上实际上有什么作用? [英] Android: What does posting a runnable from another thread onto the main thread actually do?

查看:44
本文介绍了Android:将可运行对象从另一个线程发布到主线程上实际上有什么作用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

那么,正如标题所述:当您将可运行对象从另一个线程发布到主线程时,实际上会发生什么?

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.CallbackhandleMessage()中处理.处理程序已初始化.

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.

MessageQueuewhen字段排序.在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:

    从池中获得
  1. A Message(一个简单的静态链表 在Message类中)

  1. A Message is obtained from the pool (a simple static linked list in the Message 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是 比队列的开头更早,它变成了新的开头.如果 不是,而是插入到中间,这样MessageQueuewhen

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()从队列中删除同步屏障.未标记为isAsynchronousMessages将被忽略,并且不会完全出队和处理.而是,将扫描队列,直到找到带有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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆