# Android Handler机制:Looper、Handler、MessageQueue、Message的关系 站点:答源(https://www.dandroid.cn/) 原文:https://www.dandroid.cn/archives/21116 Canonical:https://www.dandroid.cn/archives/21116 作者:admin 发布日期:2023-09-25 最后更新:2023-09-25 16:07 分类:Android系统开发 / Framework源码 内容类型:post 说明:本页是原文的纯文本镜像,供大模型与检索器完整读取;不含导航、广告、付费框。引用请以上方「原文」链接为准。 ## 正文 一、概述 Handler是Android中处理异步消息的机制。Looper、Handler、MessageQueue、Message概括来说就是:Looper负责的就是创建一个MessageQueue,然后进入一个无限循环体不断从该MessageQueue中读取消息Message,然后回调相应的消息处理函数,而消息的创建者就是一个或多个Handler,执行完成一个消息后则继续循环。 二、MessageQueue详解 消息队列MessageQueue就是存放消息的队列。那队列中存储的消息是什么呢?假设我们在UI界面上单击了某个按钮,而此时程序又恰好收到了某个广播事件,那我们如何处理这两件事呢? 因为一个线程在某一时刻只能处理一件事情,不能同时处理多件事情,所以我们不能同时处理按钮的单击事件和广播事件,我们只能挨个对其进行处理,只要挨个处理就要有处理的先后顺序。为此Android把UI界面上单击按钮的事件封装成了一个Message,将其放入到MessageQueue里面去,即将单击按钮事件的Message入栈到消息队列中,然后再将广播事件的封装成以Message,也将其入栈到消息队列中。也就是说一个Message对象表示的是线程需要处理的一件事情,消息队列就是一堆需要处理的Message的池。线程Thread会依次取出消息队列中的消息,依次对其进行处理。 MessageQueue中有两个比较重要的方法,一个是enqueueMessage方法,一个是next方法。enqueueMessage方法用于将一个Message放入到消息队列MessageQueue中,next方法是从消息队列MessageQueue中阻塞式地取出一个Message。 三、Looper详解 消息队列MessageQueue只是存储Message的地方,真正让消息队列循环起来的是Looper,这就好比消息队列MessageQueue是个水车,那么Looper就是让水车转动起来的河水,如果没有河水,那么水车就是个静止的摆设,没有任何用处,Looper让MessageQueue动了起来。 Looper是用来使线程中的消息循环起来的。默认情况下当我们创建一个新的线程的时候,这个线程里面是没有消息队列MessageQueue的。为了能够让线程能够绑定一个消息队列,我们需要借助于Looper:首先我们要调用Looper的prepare方法,然后调用Looper的loop方法。 (一)prepare()方法 public static final void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(true)); sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。可以看到,将一个Looper的实例放入了ThreadLocal,并且先判断了sThreadLocal.get是否为null,否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例。 (二)构造函数 上面的代码执行了Looper的构造函数,我们看一下其代码: private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); } 在构造函数中,创建了一个消息队列MessageQueue,并将其赋值给其成员字段mQueue,这样Looper也就与MessageQueue通过成员字段mQueue进行了关联。 (三)loop()方法 public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycle(); } } 上面有几行代码是关键代码: 1. final Looper me = myLooper(); myLooper()方法直接返回了sThreadLocal存储的Looper实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。 final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } public static Looper myLooper() { return sThreadLocal.get(); } 2. final MessageQueue queue = me.mQueue; 拿到该looper实例中的消息队列mQueue。变量me是通过静态方法myLooper()获得的当前线程所绑定的Looper,me.mQueue是当前线程所关联的消息队列。 3. for (;;) 进入了循环。我们发现for循环没有设置循环终止的条件,所以这个for循环是个无限循环。 4. Message msg = queue.next(); // might block 取出一条消息,如果没有消息则阻塞。我们通过消息队列MessageQueue的next方法从消息队列中取出一条消息,如果此时消息队列中有Message,那么next方法会立即返回该Message,如果此时消息队列中没有Message,那么next方法就会阻塞式地等待获取Message。 5. msg.target.dispatchMessage(msg); msg的target属性是Handler,该代码的意思是让Message所关联的Handler通过dispatchMessage方法让Handler处理该Message。 6. msg.recycle(); 释放消息占据的资源。 (四)Looper主要作用 1.与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。 2.loop()方法,不断从MessageQueue中去取消息,交给消息Message的target属性,即Handler的dispatchMessage去处理。 二、Handler详解 (一)构造函数 使用Handler之前,我们都是初始化一个实例,比如用于更新UI线程,我们会在声明的时候直接初始化,或者在onCreate中初始化Handler实例。所以我们首先看Handler的构造方法,看其如何与MessageQueue联系上的,它在子线程中发送的消息(一般发送消息都在非UI线程)怎么发送到MessageQueue中的。 public Handler() { this(null, false); } public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } 上面有几行代码是关键代码: 1. public Handler(Callback callback, boolean async) Handler.Callback是用来处理Message的一种手段,如果没有传递该参数,那么就应该重写Handler的handleMessage方法,也就是说为了使得Handler能够处理Message,我们有两种办法: (1)向Hanlder的构造函数传入一个Handler.Callback对象,并实现Handler.Callback的handleMessage方法。 (2)无需向Hanlder的构造函数传入Handler.Callback对象,但是需要重写Handler本身的handleMessage方法。 //在主线程中创建mHandler,所以自动绑定主线程 private Handler mHandler = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { switch (msg.what){ case 1: System.out.println("handleMessage thread id " + Thread.currentThread().getId()); System.out.println("msg.arg1:" + msg.arg1); System.out.println("msg.arg2:" + msg.arg2); System.out.println("msg.obj:" + msg.obj.toString()); System.out.println("msg.setDate:" + msg.getData().get("QQ")); textview.setText("success"); break; } } }; private Handler mHandler2 = new Handler(new Handler.Callback() { @Override public boolean handleMessage(@NonNull Message msg) { switch (msg.what){ case 1: System.out.println("handleMessage thread id " + Thread.currentThread().getId()); System.out.println("msg.arg1:" + msg.arg1); System.out.println("msg.arg2:" + msg.arg2); System.out.println("msg.obj:" + msg.obj.toString()); System.out.println("msg.setDate:" + msg.getData().get("QQ")); textview.setText("success"); break; } return false; } }); 也就是说无论哪种方式,我们都得通过某种方式实现handleMessage方法,这点与Java中对Thread的设计有异曲同工之处。 在Java中,如果我们想使用多线程,有两种办法: (1)向Thread的构造函数传入一个Runnable对象,并实现Runnable的run方法。 (2)无需向Thread的构造函数传入Runnable对象,但是要重写Thread本身的run方法。 2. mLooper = Looper.myLooper(); 首先通过Looper.myLooper()获取了当前线程保存的Looper实例。 3.mQueue = mLooper.mQueue; 然后再获取该Looper实例中保存的消息队列MessageQueue,这样就保证了Handler的实例与Looper实例中MessageQueue关联上了。 (二)sendMessage()方法 public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); } public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); } 辗转反则最后调用了sendMessageAtTime,在此方法内部有直接获取MessageQueue,然后调用了enqueueMessage方法,我们再来看看此方法: private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } 上面有几行代码是关键代码: 1. msg.target = this; 该代码将Message的target绑定为当前的Handler。 2. queue.enqueueMessage; 变量queue表示的是Handler所绑定的消息队列MessageQueue,通过调用queue.enqueueMessage(msg, uptimeMillis)我们将Message放入到消息队列中。 现在已经很清楚了Looper会调用prepare()和loop()方法,在当前执行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,然后当前线程进入一个无限循环中去,不断从MessageQueue中读取Handler发来的消息。然后再回调创建该消息的Handler中的dispathMessage方法。Handler的dispatchMessage的源码如下: public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } 上面有几行代码是关键代码: 1. if (msg.callback != null) { handleCallback(msg); } 首先会判断msg.callback存不存在,msg.callback是Runnable类型,如果msg.callback存在,那么说明该Message是通过执行Handler的postXXX系列方法将Message放入到消息队列中的,这种情况下会执行handleCallback(msg), handleCallback源码如下: private static void handleCallback(Message message) { message.callback.run(); } 这样我们我们就清楚地看到我们执行了msg.callback的run方法,也就是执行了postXXX所传递的Runnable对象的run方法。 2. else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } 如果我们不是通过postXXX系列方法将Message放入到消息队列中的,那么msg.callback就是null,代码继续往下执行,接着我们会判断Handler的成员字段mCallback存不存在。mCallback是Hanlder.Callback类型的,我们在上面的Handler构造函数提到过,在Handler的构造函数中我们可以传递Hanlder.Callback类型的对象,该对象需要实现handleMessage方法,如果我们在构造函数中传递了该Callback对象,那么我们就会让Callback的handleMessage方法来处理Message。 3.handleMessage(msg); 如果我们在构造函数中没有传入Callback类型的对象,那么mCallback就为null,那么我们会调用Handler自身的hanldeMessage方法,该方法默认是个空方法,我们需要自己是重写实现该方法。 综上,我们可以看到Handler提供了三种途径处理Message,而且处理有前后优先级之分:首先尝试让postXXX中传递的Runnable执行,其次尝试让Handler构造函数中传入的Callback的handleMessage方法处理,最后才是让Handler自身的handleMessage方法处理Message。 让我们看一下handleMessage(msg) /** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { } 可以看到这是一个空方法,为什么呢,因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。 private Handler mHandler = new Handler(){ public void handleMessage(android.os.Message msg){ switch (msg.what){ case value: break; default: break; } }; }; 到此,sendMessage方式流程已经解释完毕,接下来看下post方式。 (三)post()方法 mHandler.post(new Runnable() { @Override public void run() { Log.e("TAG", Thread.currentThread().getName()); mTxt.setText("yoxi"); } }); 然后run方法中可以写更新UI的代码,其实这个Runnable并没有创建什么线程,而是发送了一条消息,下面看源码: public final boolean post(Runnable r){ return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; } 可以看到,在getPostMessage中,得到了一个Message对象,然后将我们创建的Runable对象作为callback属性,赋值给了此message。 注:产生一个Message对象,可以new也可以使用Message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new重新分配内存。 public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); } 最终和handler.sendMessage一样,调用了sendMessageAtTime,然后调用了enqueueMessage方法,给msg.target赋值为handler,最终加入MessagQueue。 public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } 可以看到,这里msg的callback和target都有值,那么会执行msg.callback != null的判断,则执行handleCallback回调,也就是我们的Runnable对象。 三、总结 到此,这个流程已经解释完毕,让我们首先总结一下: 首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。 在Lopper构造函数中,创建了一个消息队列MessageQueue,并将其赋值给其成员字段mQueue,这样Looper也就与MessageQueue通过成员字段mQueue进行了关联。 Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。 Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。 Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。 在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。 四、一图胜千言 我们在本文讨论了Thread、MessageQueue、Looper以及Hanlder的之间的关系,我们可以通过如下一张传送带的图来更形象的理解他们之间的关系。 在现实生活的生产生活中,存在着各种各样的传送带,传送带上面洒满了各种货物,传送带在发动机滚轮的带动下一直在向前滚动,不断有新的货物放置在传送带的一端,货物在传送带的带动下送到另一端进行收集处理。 我们可以把传送带上的货物看做是一个个的Message,而承载这些货物的传送带就是装载Message的消息队列MessageQueue。传送带是靠发送机滚轮带动起来转动的,我们可以把发送机滚轮看做是Looper,而发动机的转动是需要电源的,我们可以把电源看做是线程Thread,所有的消息循环的一切操作都是基于某个线程的。一切准备就绪,我们只需要按下电源开关发动机就会转动起来,这个开关就是Looper的loop方法,当我们按下开关的时候,我们就相当于执行了Looper的loop方法,此时Looper就会驱动着消息队列循环起来。 那Hanlder在传送带模型中相当于什么呢?我们可以将Handler看做是放入货物以及取走货物的管道:货物从一端顺着管道划入传送带,货物又从另一端顺着管道划出传送带。我们在传送带的一端放入货物的操作就相当于我们调用了Handler的sendMessageXXX、sendEmptyMessageXXX或postXXX方法,这就把Message对象放入到了消息队列MessageQueue中了。当货物从传送带的另一端顺着管道划出时,我们就相当于调用了Hanlder的dispatchMessage方法,最终执行handleMessage方法,在该方法中我们完成对Message的处理。 文章来源于互联网:Android Handler机制:Looper、Handler、MessageQueue、Message的关系 --- 由 答源 DAN 底座 1.4.1 生成 · 2026-11-14T22:48:43+00:00