Handler,Message,Looper MessageQueue

article/2025/11/4 8:13:46

##1 Handler简介
Handler,Looper,Message,MessageQueue是Android开发中经常遇到的知识点,也是Android面试题中常遇到的问题。

先来看一下Message在Android中传递的概览图

Message传递示意图

上面这张图基本反映了Handler发送和处理Message的流程。Handler调用sendMessage方法,发送给MessageQueue,Looper不断从MessageQueue中取出Message,并调用Handler的handleMessage方法。
我们就根据这张图,来分析一下整个发送,轮询,处理的流程。

#2 为什么要使用Handler

  1. 绝大多数GUI系统,都是使用事件驱动编程(event driven programming)的编程范式,Android也不例外。Handler就是消息的发送者和处理者。我们平时写Activity时常用的onCreate,onStart,onResume等函数都是在Handler中被回调的(可以参见ActivityThread.java文件的实现方式),因此学习Handler可以帮助我们更好的理解Android对事件的处理,理解Android组件的生命周期。

  2. Android被设计为不允许在子线程中更新UI(会抛出异常),同时也不允许在主线程做耗时的操作(容易引起ANR)。那么当应用需要做一个耗时的操作并将耗时操作(比如说HTTP请求)的结果更新到UI的时候怎么办呢?我们可以借助于Handler机制。在主线程中定义Handler对象,并重写handleMessage方法,同时开辟子线程,在子线程中执行完耗时操作以后,调用该Handler对象的sendMessage方法,那么Handler的handleMessage方法就会在主线程中执行。

#3 Handler的基本用法
##3.1 使用sendMessage方法发送Message
先来看一下Handler的基本用法。Handler的最常用的用法就是子线程执行耗时的操作,执行完以后向主线程更新UI。
下面试一个简单的代码示例.

public class MainActivity extends ActionBarActivity {private static final String TAG = "MainActiviy";private TextView mTextView;private Handler myHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {mTextView.setText("msg.what = "+ msg.what + "  msg.obj.value = " + ((Foo)msg.obj).value);Log.d(TAG, "msg.what = "+ msg.what + "  msg.obg->value = " + ((Foo)msg.obj).value);}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mTextView = (TextView)findViewById(R.id.text_view);new Thread(new Runnable(){public void run() {for (int i = 0 ; i < 5 ; i ++) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}Message msg = myHandler.obtainMessage();msg.what = i;msg.obj = new Foo(2*i);myHandler.sendMessage(msg);}}}).start();}
}class Foo {public int value;public Foo(int value) {this.value = value;}
}

在MainActivity中定义了一个匿名内部类,继承自Handler类,并重写其中的hanleMessage方法。将发送过来的Message对象的what 和 obj的值更新到TextView上。并定义此匿名内部类的对象 myHandler.
在onCreate方法中开辟子线程,每sleep一秒钟,通过Handler对象发送一个消息。消息携带的obj为一个Foo类的对象。

运行一下这个程序,控件被成功更新,并打印出如下Log:

D/MainActiviy(17679): msg.what = 0  msg.obg->value = 0
D/MainActiviy(17679): msg.what = 1  msg.obg->value = 2
D/MainActiviy(17679): msg.what = 2  msg.obg->value = 4
D/MainActiviy(17679): msg.what = 3  msg.obg->value = 6
D/MainActiviy(17679): msg.what = 4  msg.obg->value = 8

从Log可以看出子线程里发送的消息在主线程里被成功处理。至于原理,我们会在后面进行深入的讲解。

##3.2 使用post方法发送Message
不仅可以使用Handler的sendMessage(Message msg)方法来发送一个Message对象,还可以使用post(Runnable r)方法,发送一个Runnable对象。下面是一个使用post方法的简单demo。

public class MainActivity extends ActionBarActivity {private static final String TAG = "MainActivity";Handler mHandler = new Handler();Runnable mRunable = new Runnable() {public void run() {mTextView.setText(" recevie the message");Log.D(TAG, "handler demo2 Runnable run");}};TextView mTextView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mTextView = (TextView)findViewById(R.id.text_view);new Thread(new Runnable() {public void run() {try {Thread.sleep(3000);mHandler.post(mRunable);} catch (InterruptedException e) {e.printStackTrace();}}}).start();}
}

打印出log:

D/MainActivity(26680): handler demo2 Runnable run

说明调用过post(Runnable r)方法以后,Runnable对象的run方法被调用执行。
post方法与sendMessage方法有几个不同的地方。

  1. 发送的是Runnable对象而不是Message对象
  2. 定义Handler对象的时候没有重写Handler的handleMessage方法。
  3. post方法的参数Runnable的run方法在主线程被调用,而不是handleMessage方法被调用。

上面已经用Demo的形式演示了如何使用Handler的 post 和 sendMessage方法。下一节将要学习为什么Handler可以post 一个Runnable对象,以及为什么在子线程中发送Message,handleMessage就会在主线程中被调用。

#4 Handler的原理
##4.1 消息传递的载体:Message
Message类是Handler发送消息的载体,也是MessageQueue的队列的成员。所以首先来了解一下Message类。完整的类可以参见framework的源码:Message.java。
我们只讲其中比较重要的几个属性:

public final class Message implements Parcelable {public int what;public Object obj;Handler target;Runnable callback;…
}

其中

  1. what是消息的标识符,说明消息的种类,类型。
  2. obj,obj是Message所携带的消息体。因为obj是一个Object类型的引用。在Java中,任何一个类都是派生于Object类。可以看出Message可以携带任意类型的消息。
  3. target:消息的接收者(即处理者),可以看到消息的接收者是一个Handler对象。因此可以知道在Android中,Message的接受者(处理者)也是Handler。
  4. callback,这个参数说明该Message自带处理回调,相当于自带处理方式的Message,他的类型是Runnable。

Message有很多属性,但是上述属性已经足够分析Handler原理了。

##4.2 消息的发送者和处理者:Handler
官方文档对于Handler用法的说明:
There are two main uses for a Handler:
(1) to schedule messages and runnables to beexecuted as some point in the future;
(2) to enqueue an action to be performed on a different thread than your own.

翻译成中文就是:Handler主要有两个作用:
1)是在将来的某个时间点执行Message和Runnable。
2)将动作(也就是Message和Runnable)放入消息队列,并在自己以外的另一个线程执行。
从官方的解释看出有些书本上所说的:Handler是用来从子线程向主线程更新UI,这个说法是不准确的,至少没有完全描述出Handler的作用。
###4.2.1 Handler的创建
我们调用Handler的sendMessage方法和post方法来发送消息,而在4.1节中看到Message的target属性也是Handler,所以Message的发送者和处理者都是Handler。
Handler的构造方法:

public Handler(Callback callback, boolean async) {…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;
}

在构造Handler对象的时候,首先指定调用Looper.myLooper()方法,Looper我们后面会再介绍。这里大家先了解这个是为了获取当前线程的Looper对象。把当前线程的Looper对象赋给了mLooper,并且通过mQueue = mLooper.mQueue获取当前线程的Looper所对应的MessageQueue。从这一段代码可以看出当创建Handler对象的时候,Handler对象就会和当前线程的Looper对象以及Looper对象中的MessageQueue绑定在一起。

而如果当前线程没有Looper对象,则会抛出RuntimeException,说明不允许在没有Looper对象的线程中创建Handler对象。Activity和Service所在主线程中,系统已经创建打了一个MainLooper,所以可以直接在主线程中创建Handler对象,而该Handler对象则绑定到主线程的Looper对象(即MainLooper),以及主线程Looper对象对应的MessageQueue。

###4.2.2 Handler消息的发送: sendMessage
Handler归属于sendMessage方法的函数簇的签名如下:

public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) 
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean sendMessage(Message msg)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public boolean sendMessageAtTime(Message msg, long uptimeMillis)

主要分为sendEmptyMessage和SendMessage两大类,每一类又分为直接发送,延时发送,和在指定时间发送。
首先来看sendEmptyMessage这一大类:

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {Message msg = Message.obtain();msg.what = what;return sendMessageDelayed(msg, delayMillis);
}

当调用sendEmptyMessage的时候,Handler会为构建一个空的消息体,并调用sendMeesage类方法。因此sendEmptyMessage是sendMessage方法的一个简写的形式。所以只需集中关注一下sendMessage这一类的方法。
sendMessage,sendMessageDelayed,最终都是调用到sendMessageAtTime,因此直接分析一下sendMessageAtTime方法。

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);}

方法体很简单,将参数中的Message对象,调用enqueueMessage(…)方法,Message放入MessageQueue。而所要放入的目标MessageQueue正是一开始在创建Handler的时候所绑定的对应线程的Looper对象对应的MessageQueue。
从这里可以看到调用sendMessage方法是如何将Message消息体发送到Handler所关联的MessageQueue中的。
再来看一下enqueueMessage方法

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);
}

注意到这样一行msg.target=this,在4.1中讲过target表示Message对象的处理者,很显然,Handler在将Message放入消息队列的时候,指定了Message将来的处理者就是我自己。
###4.2.3 Handler消息的发送: post
之前讲过Handler除了可以调用SendMessage方法发送Message对象,还可以调用post方法来发送Runnable对象。归属于post的函数簇的签名如下:

public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
public final boolean postDelayed(Runnable r, long delayMillis)

举一个典型的例子:

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;
}

采用post发送也是sendMessage方法的一种表现形式。首先构建了一个空的Message对象,将Message对象的callback置为传入的Runnable对象。在4.1中介绍Message介绍到Message的callback属性,这个是消息自带的处理函数。说明post一个Runnable对象的时候,也是发送了一个Message对象,只不过这个Message消息体是自带处理callback处理函数的。

###4.2.4 Handler的消息处理 dispatchMessage(Message msg)
Handler的处理函数为dispatchMessage(Message msg)

    public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}

Handler在处理收到的Message的时候,首先会查看Message是否存在callback,若存在则调用Message对象的callback处理消息。即我们在使用post方法发送消息时传入的Runnable对象。
若callback为null,则会走到handleMessage方法,即通常定义Handler类的时候重写的方法。
###4.2.5 Handler类小结
4.2节分析了Handler的构造函数,发送Message对象的方式(sendMessage,post)和处理Message的方式(dispatchMessage)。

在构造Handler对象的时候,Handler会绑定到创建Handler时候所在的线程的Looper和MessageQueue。

Handler有两种方式发送消息,post(Runnable r)和sendMessage(Message m)。post方法的核心思想是,构造一个Message对象,该Message的callback为定义的 Runnable,再将此Message对象通过sendMessage方法发送到消息队列中去。当调用Handler的sendMessage的方法的时候,Handler会将Message放入创建时绑定的MessageQueue当中,并且指定Message的target(即消息的接收者)为自己。这样保证将来这个消息仍然由自己处理。

当Handler处理消息的时候。会查询该消息是否带有callback,若消息带有callback,这样的消息通常由post(Runnable r)方法发送,则调用Runnable中定义的run()方法来处理函数。若Message若没有携带callback,则使用handleMessage来处理Message。
##4.3 MessageQueue的持有者:Looper
前面已经讲了Message本身,Message的发送,Message的处理。那么保存Message的MessageQueue又是存在于哪里的呢?而又是谁不停的从MessageQueue中读取Message,再将其处理的呢?答案就是Looper。
###4.3.1 创建Looper
Looper的构造函数是私有的,我们无法直接构建Looper对象。Looper为我们暴露的接口是prepare();

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();public static void prepare() {prepare(true);
}private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed));
}

Looper类为我们维护了一个类型为ThreadLocal的sThreadLocal变量,如果大家想深入了解,可以百度一下ThreadLocal的用法。继续阅读本文则只需要了解,Looper为每一个调用prepare()的线程创建了一个Looper对象,并且关联到当前线程。所以说一个线程默认是没有Looper对象的,而且如果在一个已经调用过Looper.parepare()方法的线程中再次调用prepare(),则会抛出异常。由此可以看出:一个线程最多只能持有一个Looper对象。
再来看一下Looper的构造函数:

private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();
}

构建Looper对象的时候的动作很简单,创建一个消息队列,并且记录当前线程。说明MessageQueue是创建在Looper当中。由之前线程和Looper对象的关系可知。一个Thread最多持有一个Looper,该Looper包含一个MessageQueue。而线程默认是没有Looper的,也不存在消息队列。

###4.3.2 轮询消息并处理

public static void loop() {…for (;;) {Message msg = queue.next(); // might block….msg.target.dispatchMessage(msg);….}
}

线程创建Looper以后,调用Looper.loop()则进入一个死循环,不停的轮询消息队列,取出Message,并调用Message的接收者(即target)的处理函数(即dispatchMessage)。

#5 总结
我们从介绍了Message这个类的属性,Handler的创建,并学习了使用Handler发送和处理Message的接口,重点是Handler在创建的时候是如何绑定到线程的Looper和MessageQueue对象的,以及是如何将Message对象放入指定的MessageQue的。最后学习了Looper对象是如何从MessageQueue中获取Message,并且调用Message的target的处理函数进行处理。

我们以回答面试的时候关于Handler的几个问题来结束本文:

1. 为什么可以发送Runnable对象?
使用post(Runnable r)方法,首先构造一个空的Message,再将Message的callback置为Runnable对象r,再调用sendMessage方法发送该Message。post方法是sendMessage方法的一个变体。而当该消息被处理的时候,因为该Message自带callback,所以callback(也即Runnable对象 r)将会被调用。
2. Message为什么会在不同的线程里进行处理,Handler是如何识别当前线程,又是如何在另外一个线程里运行的?
Handler在创建的时候会记录当前的线程,当前线程的Looper,当前线程Looper对应的MessageQue。因此Handler是绑定到一个线程的。而此后Handler发送的消息都会发送到Handler所绑定的MessageQueue当中,并且指定Message的target也就是处理者为this。该线程的Looper对象轮询到Message以后又会在该线程中调用Message.target.dispatchMessage。因此Handler可以在任意线程中发送消息,但是发送的消息都是在创建他的线程中进行处理的。
3. Looper,MessageQue,线程以及Handler他们之间是怎么样的对应关系?
线程默认是没有Looper和MessageQue的。调用Looper.prepare()以后,则为当前线程创建了一个Looper,一个Looper含有一个MessageQueue。所以一个线程最多持有一个Looper和一个MessageQueue。
Handler在创建的时候会绑定到当前线程的Looper,MessageQueue
4. Looper取到Message以后又是如何找到发送他的Handler,再调用对应的handleMessage方法的呢?
Looper在调用loop()方法进入消息轮询以后,从消息队列中取出Message,调用Message.target.dispatchMessage,由于在使用Handler发送消息的时候指定了消息的target是this引用,所以该消息最终将由发送该消息的Handler的dispatchMessage方法进行处理。


http://chatgpt.dhexx.cn/article/fPHQZU8e.shtml

相关文章

message hook

简单的 WH_GETMESSAGE 钩子的使用。 MessageHook.h #ifndef __MessageHook_h__#define __MessageHook_h__#include <Windows.h>#include <tchar.h>#ifdef __cplusplusextern "C" {#endif#ifdef DLL_EXPORT#define DLLAPI __declspec(dllexport)#else#d…

Android Message机制

Android Message机制 ​ Android的Message机制&#xff0c;也叫handler机制&#xff0c;主要由3部分Message组成&#xff0c;这三部分分别是Looper、MessageQueue和Handler。 ​ Looper&#xff1a;循环体&#xff0c;其作用就是从MessageQueue中取出Message进行消费&#xf…

Windows 消息循环 GetMessage() PeekMessage()

GetMessage()函数的消息循环的汇编代码.while TRUEinvoke GetMessage,addr stMsg,NULL,0,0.break .if eax 0invoke TranslateMessage,addr stMsginvoke DispatchMessage,addr stMsg.endwret解读&#xff1a; 调用 GetMessage 函数,函数中都要用到一个MSG结构体&#xff0c;那是…

WindowsMessage

消息介绍 Windows是一个消息&#xff08;Message&#xff09;驱动系统&#xff0c;它不是由事件的顺序来控制&#xff0c;而是由事件的发生来控制&#xff0c;而这种事件的发生是随机的、不确定的&#xff0c;并没有预定的顺序&#xff0c;这样就允许程序的用户用各种合理的顺序…

从内核层说清GetMessage , DispatchMessage

文章目录 要点回顾&#xff1a;为什么拿到句柄非得要回零环&#xff1f;消息队列&#xff08;总共有7个小队列&#xff09;结构GetMessage的声明&#xff1a;GetMessage进入内核&#xff1a;GetMessage的功能总结&#xff1a;DispatchMessage举例验证&#xff08;有前提情况&am…

Message的消息池(sPool)

关键总结 1、消息池缓存有可重复使用的消息实例&#xff0c;避免过多的创建与回收消息实例 2、消息池是一个栈&#xff08;LIFO/FILO后进先出/先进后出&#xff09;的数据结构&#xff0c;具体的数据存放是采用了链表方式 3、消息池一开始是空的&#xff0c;需要主动添加消息进…

信息炸弹——Message Boom

前言 好的今天我们来讨论一下什么叫信息炸弹。 可能之前看过我文章的小伙伴们在想&#xff0c;这种听名字就具有攻击性的东西为什么不把它放到黑客七宗罪专栏里&#xff1f; 毕竟这只是个脚本&#xff0c;对于个人账号的攻击性确实强&#xff0c;聊天记录也会占用硬盘。但是…

handler+message【消息机制】

&#x1f356;&#x1f356; 作者 &#xff1a; 不良使 &#x1f356;&#x1f356;&#x1f356;&#x1f356; 潜力创作新星 华为云享专家 &#x1f356;&#x1f356;&#x1f356;&#x1f356;&#x1f356; PythonAndroid &#x1f356;&#x1f356;&#x1f356;&#…

c++中MessageBox弹窗的用法大全

想必大家都知道&#xff0c;MessageBox函数是c语言中很常用且好玩的函数之一&#xff0c;那你知道它怎么用吗&#xff1f; 这是MessageBox函数的标准格式之一&#xff0c;本人喜欢用这种格式&#xff0c;注意函数的大小写&#xff01; MessageBox不在 #include<bits/stdc.h&…

message broker

MB概述 MB的全称是message broker&#xff0c;即“消息代理”。“消息”一词前几年比较火&#xff0c;消息中间件也卖的很火&#xff0c;当时似乎J2EE的产品都要跟“消息”、“中间件”扯上点关系&#xff0c;以彰显潮流。我觉得初学者只需记住“消息”的异步性即可&#xff0c…

MP3音频文件格式(MPEG-1 audio layer 3)

MP3音频文件格式 【百度百科】mp3 &#xff08;一种音频编码方式&#xff09; 【维基百科】MP3&#xff08;本文重定向自 MPEG-1 Audio Layer 3&#xff09; MP3(MPEG-1 audio layer 3) MPEG-1音频分三层&#xff0c;分别为 MPEG-1 Layer1&#xff0c;MPEG-1 Layer2 以及 MPE…

怎么把wav文件改成mp3?

怎么把wav文件改成mp3&#xff1f;有过摄像摄影经历的小伙伴都应该认识wav&#xff0c;wav就是他们作品的保存格式。因为wav格式的文件体积特别大&#xff0c;在储存的时候会占用我们大量的内存&#xff0c;而且为了播放方便&#xff0c;我们通常要把wav文件改成mp3格式的&…

音频文件如何转成mp3格式

当提到音频文件格式时&#xff0c;大家往往会想到最为流行和广泛使用的mp3格式。mp3是一种广受欢迎的音频格式&#xff0c;因为各种音频格式自身特点的原因&#xff0c;所以将其他格式的音频文件转换成mp3是非常普遍的需求。就比如在我们日常生活中&#xff0c;下载到的各种格式…

如何转换音频格式为mp3?

一提到音乐&#xff0c;大家先想到的应该就是MP3了&#xff0c;既然MP3作为常用的&#xff0c;被大家所熟知的一种音频格式&#xff0c;那它必定有其他格式无可比拟的优点。其实mp3从功能上来讲它具有更强的携带性和传输性&#xff0c;利于保存和分享&#xff1b;其次MP3本身的…

电脑音频转换mp3格式怎么弄,教你音频怎么转换mp3格式

mp3格式是目前几乎全兼容的格式了&#xff0c;在我们参加一些会议或讲座时&#xff0c;需要录制一些重要的信息&#xff0c;结束后再进行复盘或分享。然而&#xff0c;不同的录制工具录制的音频格式也不同&#xff0c;这时使用软件将音频统一成mp3格式的话&#xff0c;就会方便…

免费在线MP3转换器:将音乐文件转换为MP3格式

在今天的数字时代&#xff0c;音乐成为了人们生活中不可或缺的一部分。然而&#xff0c;由于音乐文件格式的不同&#xff0c;我们有时可能无法在不同的设备上播放我们最喜爱的歌曲。MP3格式作为最常用的音乐文件格式之一&#xff0c;通常可以被几乎所有的设备支持&#xff0c;因…

mp3格式怎么弄?分享三个音频文件格式转换的方法

不知道小伙伴们有没有遇到过这样的情况&#xff0c;在网上下载一首歌下来&#xff0c;正想打开&#xff0c;结果却发现我们的播放器无法播放。你们知道这是为什么嘛&#xff0c;其实我们的音频文件是有很多不同的格式&#xff0c;其中就有些比较少见的格式&#xff0c;我们的音…

如何从MP4视频文件中抽取MP3音频?

简 介&#xff1a; 为了能够处理视频中的音频&#xff0c;测试了两种提取视频中的音频方法。一种是利用格式工程软件另外一种利用ffmpeg软件。 关键词&#xff1a; 视频文件&#xff0c;音频文件&#xff0c;mp4&#xff0c;mp3 #mermaid-svg-sPs0isryqtLTjZyg {font-family:&q…

如何将音频文件转换为MP3格式?

音频文件有很多种格式&#xff0c;如 WAV、FLAC、AAC 等&#xff0c;其中 MP3 是最为常见的一种格式&#xff0c;因为它具有压缩比高、音质损失少、兼容性强等优点&#xff0c;适合在各种设备上播放。如果你想将一个音频文件转换为 MP3 格式&#xff0c;可以采用以下几种方法&a…

Next() Nextline() hasNext()区别

next类和hasNext方法遇到缓冲区没数据时&#xff0c;会阻塞&#xff0c;等待输入后next类会读取&#xff0c;hasNext会返回true 1&#xff09;nextLine nextLine&#xff08;&#xff09;方法返回的是"\n"之前的所有字符&#xff0c;它是可以得到带空格的字符串的。 …