源码分析Android Handler是如何实现线程间通信的

Handler作为Android消息通信的基础,它的使用是每一个开发者都必须掌握的。开发者从一开始就被告知必须在主线程中进行UI操作。但Handler是如何实现线程间通信的呢?本文将从源码中分析Handler的消息通信机制。

0x00 Handler使用

首先看看我们平时是如何使用的Handler的。先看看以下代码

//定义Handler
Handler mHandler = new Handler(){
public void handleMessage(Message msg){
switch(msg.what){
case UPDATE_UI:
updateUI(msg);
break;
}
}
};
class MyThread extends Thread{
public void run(){
//do same work!
...
//send message
Message msg = mHandler.obtainMessage(UPDATE_UI);
mHandler.sendMessage(msg);
}
} private void updateUI(Message msg){
//update UI
}

在子线程中sendMessage(Message)发送消息,然后在Handler的handleMessage(Message)接收消息,执行更新UI操作。那么Handler是如何把消息从MyThread传递到MainThread中来呢?我们从sendMessage()开始慢慢揭开它的面纱。

0x01 sendMessage(Message)

public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
...
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);
}
...
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

我们发现调用sendMessage()方法最后都走到enqueueMessage()这个方法,一开始就把当前Handler实例赋给了Message.target的属性里面,后面可以知道这个target是用来执行处理函数回调的。

enqueueMessage方法是把Message信息放入到一个MessageQueue的队列中。顾名思义MessageQueue就是消息队列。从sendMessageAtTime()方法知道这个MessageQueueHandler中的一个成员。它是在Handler的构造函数中通过Loopger对象来初始化的。

0x02 Handler构造函数

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

这时候我们脑海知道创建Handler的时候,同时也创建了Looper实例和MessageQueue引用(MessageQueue对象其实是在Looper中构造的)。Looper是何物呢?简单地说就是消息循环,这个我们稍后会分析。

0x03 enqueueMessage(MessageQueue)

boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
} synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
} msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
//这里把消息插入到队列中
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
} // We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

MessageQueue中可以看到这个入列方法中有一个for循环就是把当前的需要处理Message放到队列的合适位置。因为需要处理的Message对象都有一个开始处理的时间when,这个队列是按照when排序的。

至此,Handler调用sendMessage()方法后就把Message消息通过enqueueMessage()插入MessageQueue队列中。

而这个MessageQueue是在Looper中维护的。

0x04 prepare()创建Looper

0x02中我们知道创建Handler时就使用静态方法Looper.myLooper()得到当前线程的Looper对象。

/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

sThreadLocal是一个ThreadLocal类型的静态变量。什么时候会把Looper对象放在sThreadLocal中呢?通过prepare()方法。

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在构造函数中创建MessageQueue对象

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

调用prepare()方法将一个Looper对象放在了静态的ThreadLocal对象中。这个是一个与线程绑定的对象,且在内存中仅保存了一份引用。

使用ThreadLocal对象这一点非常巧妙,也非常重要,这是线程间通信的基础。即在线程中调用prepare()时就在该线程中绑定了Looper对象,而Looper对象中拥有MessageQueue引用。所以每个线程都有一个消息队列

这样HandlerLooperMessageQueue这几个类关系大概就可以画出来了。

0x05 启动循环loop()

/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the 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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
} final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
//执行处理消息的回调
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
} 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.recycleUnchecked();
}
}

loop()方法中有一个无限循环,不停地读取调用MessageQueuenext()方法。当next()没有返回时就阻塞在这里。当获取到MessageQueue中的消息时,就执行了处理消息的回调函数msg.target.dispatchMessage(msg)

前面0x01分析我们知道msg.target是在Handler中的enqueueMessage()进行赋值,即它指向当前的Handler实例。

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

执行msg.target.dispatchMessage(msg)后便走到了以下流程

/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

这里就是回调handleMessage(msg)函数处理消息的地方。Handler负责将Message入列,Looper则负责循环从MessageQueue中取出需要处理的Message并交由Handler来处理。

0x06 启动主线程的消息循环

我们知道通过静态方法Looper.prepare()创建了绑定当前线程的Looper对象,而通过loop()启动一个循环不停地读取队列中Message。但是Android系统是什么时候启动了主线程的消息循环呢?

要理解这一点就必须进入Android应用程序的入口ActivityThreadmain方法。

public static void main(String[] args) {
... Looper.prepareMainLooper(); ...
Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited");
}

可以看出main方法中先后执行了Looper.prepareMainLooper()方法和Looper.loop()方法。正常情况下main方法不会退出,只有loop()方法发生异常后将会抛出RuntimeException

0x07 Looper.prepareMainLooper()

/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

prepareMainLooper()方法其实是调用了prepare()方法。

当我们启动应用时系统就调用了prepareMainLooper()并在主线程中绑定了一个Looper对象。

这时候我们回过来看看一开始的Handler使用方式。在主线程中我们创建了Handler对象,在Handler构造函数中初始化了Looper(即获取到了绑定在主线程中的Looper对象)。当在子线程MyThread中通过mHandler.sendMessage(msg)方法发送一个消息时就把Message放在与主线程绑定的MessageQueue中。这样在子线程中使用Handler就实现了消息的通信。

可以简单的使用以下类图表示,每个线程都由一个Handler,每个Handler都是与当前所在线程的Looper绑定

0x08 主线程是否会阻塞

0x06中知道在ActivityTheadmain方法中启动了一个死循环。那主线程是不是就一直阻塞在这里呢?其实不然。可以看到ActivityThread类里面有一个自定义的Handler对象mH,在这里对象中handleMessage()回调中定义了Activity的各种交互如管理Activity生命周期,启动service,显示window等,都是通过Handler进行处理的。同时可以看出只有当应用退出EXIT_APPLICATION之后才回调用Looper.quit()停止消息循环。

public void handleMessage(Message msg) {
...
switch (msg.what) {
case LAUNCH_ACTIVITY: {
...
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
...
case PAUSE_ACTIVITY: {
...
handlePauseActivity((IBinder) args.arg1, false,
(args.argi1 & USER_LEAVING) != 0, args.argi2,
(args.argi1 & DONT_REPORT) != 0, args.argi3);
...
} break; ...
case SHOW_WINDOW:
...
handleWindowVisibility((IBinder)msg.obj, true);
...
break;
...
case EXIT_APPLICATION:
if (mInitialApplication != null) {
mInitialApplication.onTerminate();
}
Looper.myLooper().quit();
break;
...
}
...
}

0x09 总结

当创建Handler时将通过ThreadLocal在当前线程绑定一个Looper对象,而Looper持有MessageQueue对象。执行Handler.sendMessage(Message)方法将一个待处理的Message插入到MessageQueue中,这时候通过Looper.loop()方法获取到队列中Message,然后再交由Handler.handleMessage(Message)来处理。

微信关注我们,可以获取更多

源码分析Android Handler是如何实现线程间通信的的更多相关文章

  1. 鸿蒙内核源码分析(进程通讯篇) | 九种进程间通讯方式速揽 | 百篇博客分析OpenHarmony源码 | v28.03

    百篇博客系列篇.本篇为: v28.xx 鸿蒙内核源码分析(进程通讯篇) | 九种进程间通讯方式速揽 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 自旋锁当 ...

  2. Android Handler处理机制 ( 一 )(图+源码分析)——Handler,Message,Looper,MessageQueue

    android的消息处理机制(图+源码分析)——Looper,Handler,Message 作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习 google大牛们的设计思想. ...

  3. Android源码分析笔记--Handler机制

    #Handler机制# Handler机制实际就是实现一个 异步消息循环处理器 Handler的真正意义: 异步处理 Handler机制的整体表述: 消息处理线程: 在Handler机制中,异步消息处 ...

  4. HDFS源码分析心跳汇报之BPServiceActor工作线程运行流程

    在<HDFS源码分析心跳汇报之数据结构初始化>一文中,我们了解到HDFS心跳相关的BlockPoolManager.BPOfferService.BPServiceActor三者之间的关系 ...

  5. vscode源码分析【七】主进程启动消息通信服务

    第一篇: vscode源码分析[一]从源码运行vscode 第二篇:vscode源码分析[二]程序的启动逻辑,第一个窗口是如何创建的 第三篇:vscode源码分析[三]程序的启动逻辑,性能问题的追踪 ...

  6. JNI-从jvm源码分析Thread.interrupt的系统级别线程打断原理

    前言 在java编程中,我们经常会调用Thread.sleep()方法使得线程停止运行一段时间,而Thread类中也提供了interrupt方法供我们去主动打断一个线程.那么线程挂起和打断的本质究竟是 ...

  7. Android源码分析之Handler

    接上一篇分析,正如Android doc所说,Handler主要有2方面用处: 1. delay执行同一线程中的某个操作,也就是schedule message.runnable在未来的某一时刻执行: ...

  8. android的消息处理机制(图文+源码分析)—Looper/Handler/Message[转]

    from:http://www.jb51.net/article/33514.htm 作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想.andro ...

  9. Android -- 消息处理机制源码分析(Looper,Handler,Message)

    android的消息处理有三个核心类:Looper,Handler和Message.其实还有一个Message Queue(消息队列),但是MQ被封装到Looper里面了,我们不会直接与MQ打交道,因 ...

随机推荐

  1. Backdoor CTF 2013: 电子取证 250

    0x00 题目 h4x0r厌烦了你对他的城堡的所有攻击,所以他决定报复攻击你,他给你发来一封带有图片的邮件作为警告,希望你能找出他的警告消息:-) 消息的MD5值就是flag. 0x01 解题法1 给 ...

  2. Hadoop权威指南:HDFS-目录,查询文件系统,删除文件

    Hadoop权威指南:HDFS-目录,查询文件系统,删除文件 [TOC] 目录 FileSystem实例提供了创建目录的方法 public boolean mkdirs(Path f) throws ...

  3. [CSS3] 学习笔记-背景与边框相关样式

    1.与背景相关的新增属性 包括:backgroud-clip,backgroud-origin,backgroud-size <!DOCTYPE html> <html> &l ...

  4. 微服务框架概览之 Netty

    Netty 是什么 Netty 提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序 Netty 架构图 为什么选择Netty 通过对Netty的分析,我 ...

  5. W3Cschool学习笔记——CSS教程

    CSS 概述 CSS 指层叠样式表 (Cascading Style Sheets) 样式定义如何显示 HTML 元素 样式通常存储在样式表中 把样式添加到 HTML 4.0 中,是为了解决内容与表现 ...

  6. HQL连接查询

    HQL提供了连接查询机制如内连接,外连接,,还允许显示指定迫切内连接,和迫切外联结. 连接类型 内连接 inner join 或join 迫切内链接 inner join fetch 左外联结  le ...

  7. 使用 position:sticky 实现粘性布局

    如果问,CSS 中 position 属性的取值有几个?大部分人的回答是,大概是下面这几个吧? { position: static; position: relative; position: ab ...

  8. hibernate 为什么持久化类时必须提供一个不带参数的默认构造函数

    因为hibernate框架会调用这个默认构造方法来构造实例对象..即Class类的newInstance方法 这个方法就是通过调用默认构造方法来创建实例对象的 ,另外再提醒一点,如果你没有提供任何构造 ...

  9. Visual Studio命令行创建库文件lib

    Visual Studio命令行创建库文件lib OS:win7 旗舰版SP1 64位 编译器: VS 2013 express 的cl 建一个文件Static_Lib.h,源代码如下 #ifndef ...

  10. MVC - 单点登录中间件

    本章将要和大家分享的是一个单点登录中间件,中间件听起来高深其实这里只是吧单点登录要用到的逻辑和处理流程封装成了几个方法而已,默认支持采用redis服务保存session的方式,也可以使用参数Func& ...