源码分析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. Javascript/js的相等和不等运算符(= 、== 、===)

    "=="和"==="运算符用于比较两个值是否相等,当然它们对相等的定义不尽相同.两个运算符允许任意类型的操作数,如果操作数相等则返回tru,否则返回false. ...

  2. html 一般标签 常用标签 表格

    body的属性: bgcolor                页面背景色 background            背景壁纸.图片 text                    文字颜色 top ...

  3. Windows服务的创建、安装、卸载

    1.新建Window服务项目 2.添加安装配置文件 3.serviceProcessInstaller1右键属性,设置Account属性为LocalSystem. serviceInstaller1右 ...

  4. 最近一年多我总结的常用mate标签-常用mate标签

    昨天开始上班  ,今天晚上不是太忙 ,来写篇博客了 meta元素共有三个可选属性(http-equiv.name和scheme)和一个必选属性(content),content定义与 http-equ ...

  5. eNSP仿真学习,网络入门!

    为了简单的认识Internet的框架的整体结构,简单学习华为的eNSP软件来高度模拟仿真网络框架!(华为和思科公司都发布了自己的网络设备仿真软件,当然我就用国产的吧~) 华为官方的eNSP学习论坛网站 ...

  6. java的位运算符

    1.与运算&,同为1为1,否则为0: 例如:10001(二进制)&10000(二进制)=10000(二进制) 2.或运算|,只要有1就是1: 例如:10001(二进制)&100 ...

  7. Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSInvocation setArgument:atIndex:]: index (3) out of bounds [-1, 2]'

    这是相机调用方法的时候参数错误

  8. angular 输入框实现自定义验证

    此插件使用angular.js.JQuery实现.(jQuery的引入需在angular 之前) 用户可以 在输入框输入数据后验证 必填项.整数型.浮点型验证. 如果在form 里面的输入框验证,可以 ...

  9. 决策树和基于决策树的集成方法(DT,RF,GBDT,XGB)复习总结

    摘要: 1.算法概述 2.算法推导 3.算法特性及优缺点 4.注意事项 5.实现和具体例子 内容: 1.算法概述 1.1 决策树(DT)是一种基本的分类和回归方法.在分类问题中它可以认为是if-the ...

  10. 如何居中div?如何居中一个浮动元素?

    如何居中div? 给div设置一个宽度,然后添加margin:0 auto属性 div{ width:200px; margin:0 auto; } 如何居中一个浮动元素? <!DOCTYPE ...