Android中的Looper,Handler和MessageQueue之间有什么关系?
我已经检查了Looper
, Handler
和MessageQueue
的官方Android文档/指南。 但我无法得到它。 我是Android新手,对这些概念感到困惑。
Looper
是一个消息处理循环:它读取和处理MessageQueue
项目。 Looper
类通常与HandlerThread
( Thread
的子类)结合使用。
Handler
程序是一个实用程序类,便于与Looper
进行交互 – 主要通过将消息和Runnable
对象发布到线程的MessageQueue
。 当一个Handler
被创build时,它被绑定到一个特定的Looper
(和相关的线程和消息队列)。
在典型的用法中,创build并启动一个HandlerThread
,然后创build一个Handler
对象(或多个对象),其他线程可以与HandlerThread
实例进行交互。 Handler
必须在HandlerThread
上运行时创build,尽pipe一旦创build,就不会限制哪些线程可以使用Handler
的调度方法( post(Runnable)
等)
Android应用程序中的主线程(又名UI线程)在创build应用程序实例之前设置为处理程序线程。
除了class级文件,这里有一个很好的讨论。
众所周知,直接从android 主线程以外的线程 更新UI组件是非法的。 这个android文档( 在UI线程中处理昂贵的操作 )提出了如果我们需要启动一个单独的线程来完成一些昂贵的工作并更新UI之后要遵循的步骤。 这个想法是创build一个与主线程关联的Handler对象,并在适当的时候向其发布Runnable 。 这个Runnable
将在主线程上被调用。 这个机制是用Looper和Handler类实现的。
Looper
类维护一个MessageQueue ,它包含一个列表消息 。 Looper的一个重要特征就是它与Looper
创build的线程 相关联 。 这个协会是永远保存下来的 ,不能被破坏也不能改变。 另外请注意,一个线程不能与多个 Looper
关联 。 为了保证这种关联, Looper
存储在线程本地存储中,并且不能通过它的构造函数直接创build。 创build它的唯一方法是在Looper
上调用prepare静态方法。 编写方法首先检查当前线程的ThreadLocal ,以确保没有与线程关联的Looper。 检查后,创build一个新的Looper
并保存在ThreadLocal
。 准备了Looper
,我们可以调用循环方法来检查新的消息,并用Handler
来处理它们。
如名称所示, Handler
类主要负责处理(添加,删除,调度)当前线程的MessageQueue
。 Handler
实例也绑定到一个线程。 Handler和Thread之间的绑定是通过Looper
和MessageQueue
实现的。 一个Handler
总是绑定到一个Looper
,然后绑定到与Looper
相关的线程 。 与Looper
不同,多个Handler实例可以绑定到同一个线程。 无论何时我们在Handler
上调用post或任何方法,都会向关联的MessageQueue
添加一条新消息。 消息的目标字段被设置为当前的Handler
实例。 当Looper
接收到这个消息时,它会在消息的目标字段上调用dispatchMessage ,以便消息返回给Handler实例进行处理,但在正确的线程上。 Looper
, Handler
和MessageQueue
之间的关系如下所示:
我们从Looper开始。 当你了解Looper是什么时,你可以更容易理解Looper,Handler和MessageQueue之间的关系。 你也可以更好地理解Looper在GUI框架中的作用。 活套是做2件事情。
1)Looper 将一个正常的线程转换成一个正常的线程 ,该线程在run()
方法返回时终止, 直到Android应用程序正在运行 ,这在GUI框架中是需要的(技术上, run()
方法返回时它仍然终止。我澄清我的意思,下面)。
2)Looper 提供了一个排队的工作,在GUI框架中也是需要排队的。
如您所知,启动应用程序时,系统会为应用程序创build一个称为“main”的执行线程,Android应用程序通常完全在单个线程上运行,默认为“主线程”。 但主线不是一些秘密,特殊的线程 。 这只是一个普通的线程,你也可以使用new Thread()
代码来创build,这意味着它的run()
方法返回时它会终止! 想想下面的例子。
public class HelloRunnable implements Runnable { public void run() { System.out.println("Hello from a thread!"); } public static void main(String args[]) { (new Thread(new HelloRunnable())).start(); } }
现在,让我们将这个简单的原则应用到Android应用程序。 如果Android应用程序在普通线程上运行会发生什么? 一个称为“主”或“用户界面”或任何启动应用程序,并绘制所有用户界面的线程。 所以,第一个屏幕显示给用户。 所以现在怎么办? 主线程终止? 不,不应该。 它应该等到用户做了什么,对吧? 但是我们怎样才能做到这一点呢? 那么,我们可以尝试使用Object.wait()
或Thread.sleep()
。 例如,主线程完成其初始工作以显示第一个屏幕,然后睡觉。 它醒来,意味着中断,当一个新的工作要做。 到目前为止这么好,但是现在我们需要一个类似队列的数据结构来保存多个作业。 考虑一个用户连续触摸屏幕的情况,任务需要较长的时间才能完成。 所以,我们需要有一个数据结构,以先进先出的方式进行工作。 另外,你可能会想象,使用中断来实现永不间断运行的线程并不容易,而且会导致代码复杂且不可维护。 我们宁愿为此目的创build一个新机制, 这就是Looper的全部内容 。 Looper类的官方文档说:“线程默认没有与它们相关的消息循环”,Looper是一个“用于为线程运行消息循环”的类。 现在你可以明白它的意思了。
我们来看看Handler和MessageQueue。 首先,MessageQueue是我上面提到的队列。 它驻留在一个Looper中,就是这样。 你可以用Looper类的源代码来检查它。 Looper类有一个MessageQueue成员variables。
那么,Handler是什么? 如果有一个队列,那么应该有一个方法,使我们能够排队一个新的任务队列,对吗? Handler就是这样做的。 我们可以使用各种post(Runnable r)
方法将新任务排入队列(MessageQueue)。 而已。 这是关于Looper,Handler和MessageQueue的。
我的最后一句话是,所以基本上Looper是一个解决GUI框架中出现的问题的类。 但是这种需求也可能发生在其他情况下。 实际上它是一个非常着名的multithreading应用程序模式,您可以在Doug Lea的“Java中的并行编程”中了解更多关于它的信息(尤其是4.1.4章节“工作线程”将会有所帮助)。 另外,你可以想象这种机制在Android框架中并不是唯一的,但是所有的GUI框架可能都需要类似于这个。 您可以在Java Swing框架中find几乎相同的机制。
MessageQueue
:它是一个低级别的类,它包含由Looper
分派的消息列表。 消息不直接添加到MessageQueue
,而是通过与Looper
关联的Handler
对象。[ 3 ]
Looper
:循环遍历一个MessageQueue
,其中包含要分派的消息。 pipe理队列的实际任务由负责处理(添加,移除,分派)消息队列中的消息的Handler
程序完成[ 2 ]。
Handler
:它允许您发送和处理与线程的MessageQueue
关联的Message
和可运行对象。 每个Handler实例都与单个线程和该线程的消息队列相关联[ 4 ]。
当你创build一个新的Handler
,它绑定到正在创build它的线程的线程/消息队列 – 从这一点开始, 它将把消息和可运行的消息传递给消息队列,并在消息出来时执行它们队列 。
为了更好的理解,请通过下面的图片[ 2 ]。