Handler 相信每个作 Android 开发的小伙伴都很是熟悉了,最经常使用的场景就是在子线程中进行数据操做而后经过 Handler 消息机制通知到 UI 线程来更新 UI ,地球人都知道在子线程中更新 UI 通常状况下都会报错。是吧!咱用的贼溜,各类姿式发送 Handler 消息都熟练掌握,可是若是这时候出去面试被问到“ Handler 原理”,“消息是怎么从子线程发送到主线程的”等等 Handler 底层的实现,就懵逼了。java
虽然网上关于分析 Handler
的博客文章很是多,已经有不少大佬分析的很是透彻清楚了,但我这里仍是想在看过大佬们的文章后本身再写一篇,一方面是让本身加深理解,另外一方面就是想分享所学知识(分享会让知识变的更多)。android
看了不少大佬去大厂面试的面经,Handler几乎是必问的,因此咱们更加必须知其因此然了。git
简单来讲Handler是结合线程消息队列来发送和处理 Message
, Runnable
对象来实现线程间通讯的工具。面试
先来一块儿看一个关于 Handler 使用的典型实例(实例来源 Gityuan 博客)c#
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
//TODO 定义消息处理逻辑.
}
};
Looper.loop();
}
}
复制代码
在线程中声明一个Handler必须按如下顺序三步走:app
以上三步曲我称之为:Looper 肉夹馍
(手动狗头)异步
这里产生一个问题了:async
Question 1: 建立 Handler
以前为啥必定要先调用 Looper.prepare()
呢?ide
咱们带着这个问题继续往下看。工具
提及 Handler 那就必需要了解 Android 的消息机制了, Android 中有大量的交互场景是经过消息机制来驱动的,如咱们最熟悉不过的 Activity
的生命周期等等。而 Handler 就是 Android 消息机制的重要组成部分。
咱们先来看一张图:
图中描述的是消息是怎样从子线程流到主线程从而完成线程间通讯的大概流程。Android 整个消息机制主要由Handler
,Looper
,MessageQueue
,Message
四个部分组成。这里先简单介绍一下这四位具体职责:
Handler : 负责发送消息和接收消息并进行处理。
MessageQueue : 消息队列,负责消息存储与管理。
Looper : 负责关联 Handler
当前所处线程,进行消息分发,经过不断循环实现将消息从消息队列取出,而后分发给对应的消息处理者。
Message : 消息载体。
看完上图脑海中应该对 Android 的消息机制已经有了一个大概的概念了,下面咱们开始从源码来看 Android 的消息机制具体实现。这里就以上图中的主线程和子线程通讯为思路。
先来看上图对应的代码实例
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//todo UI线程接收消息并处理
Log.d("MainActivity","handleMessage....");
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Thread(){
@Override
public void run() {
super.run();
//子线程发送消息
handler.sendMessage(new Message());
}
}.start();
}
}
复制代码
主( UI )线程中实例化一个 Handle
对象。咱们跟到源码中看一下 Handle()
作了什么事情。
Handle
默认构造方法public Handler() {
this(null, false);
}
复制代码
Handler(Callback callback, boolean async)
public Handler(Callback callback, boolean async) {
......
//获取当前Looper对象
mLooper = Looper.myLooper();
//检查当前线程Looper是否存在
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
......
}
复制代码
源码解析:
在该构造方法中,每次实例化 Handler
都会检查当前线程是否已绑定 Looper
,若是没有则会抛出异常提示你没有调用 Looper.Prepare()
。看到这里【1.2】的 Question 1`是否是就有答案了。
Question 1 正解:
在建立
Handler
对象以前必需要先建立Looper
而且将Looper
对象绑定到当前线程,不然在实例化Handler
时会报异常。
那么 Looper.Prepare()
方法里又是怎么建立 Looper
对象并绑定到当前线程的呢?跟进源码看一下。
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//检查当前线程是否已有 Looper,每一个线程只容许执行一次该方法,第二次执行时线程的TLS已有数据,则会抛出异常。
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//建立Looper对象,并保存到当前线程的TLS区域
sThreadLocal.set(new Looper(quitAllowed));
}
复制代码
源码解析:
prepare()
方法默认会调用 prepare(true)
。
prepare(boolean quitAllowed)
方法参数quitAllowed
表示是否当前Looper
是否可退出,传true
表示可退出,false
为不可退出。
prepare(boolean quitAllowed)
方法中会先检查当前线程是否已经有 Looper
,若是有则会抛出异常,也就意味着每一个线程中 Looper.Prepare()
方法只能够调用一次!这里咱们就知道了,
Looper
和当前线程是一一对应的关系,一个Looper
只能关联一个线程。
Looper
后 sThreadLocal.set(new Looper(quitAllowed))
就开始建立一个 Looper
对象并保存到当前线程的TLS(关于 ThreadLocal 请自行查阅资料)区域,这样就完成了 Looper
对象的建立和关联到当前线程。Looper.Prepare()
调用后 Looper
建立好了,咱们再回到 Handler
构造方法中:
Looper.myLooper()
方法
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
复制代码
源码解析: 这个方法很简单就是去当前线程的中的 TLS 区域 Get Looper
对象,没错就是 Looper.Prepare()
方法中sThreadLocal.set(new Looper(quitAllowed))
Set 进去的。
最后 mQueue = mLooper.mQueue
会获取 Looper
对象的消息队列,若是你刚才从 prepare(boolean quitAllowed)
方法中继续跟进 new Looper(quitAllowed)
方法中你就会发现:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
复制代码
建立 Looper
对象时会为当前 Looper
对象建立一个消息队列 new MessageQueue(quitAllowed)
。
不知道细心的你发现了没,咱们的图一的代码实例中是否是少了点什么。
没错,少了肉夹馍!
呐!Question 2来啦:
不是反复强调建立 Handler
以前要先调用 Looper.Prepare()
建立 Looper
吗?那图一的代码实例中主线程并无调用啊,直接就建立了 Handler
,你这代码确定会抛这个异常:
RuntimeException( "Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
复制代码
悄咪咪告诉你,若是你在图一的代码实例中建立Handler
以前调用 Looper.Prepare()
又会抛出如下异常哦:
RuntimeException("Only one Looper may be created per thread");
复制代码
答案在 ActivityThread.main()
中。
ActivityThread
的main()
方法就是整个APP的入口,也就是咱们一般所讲的主线程
,UI线程
。 但他实际上并非一个线程,ActivityThread
并无继承Thread
类,咱们能够把他理解为主线程的管理者,负责管理主线程的执行,调度等操做,能够看下这个类源码的注释。
/** * This manages the execution of the main thread in an * application process, scheduling and executing activities, * broadcasts, and other operations on it as the activity * manager requests. * * {@hide} */
public final class ActivityThread extends ClientTransactionHandler {
......
}
复制代码
好了,咱们来看下 ActivityThread.main()
一探究竟(这里只贴关键代码了)。
public static void main(String[] args) {
.......
Looper.prepareMainLooper();
.......
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
.......
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
复制代码
源码解析:
Looper.prepareMainLooper();
Looper
,内部实现调用了 prepare(false)
方法。传 false
表示当前 Looper
不可退出。/** * 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();
}
}
复制代码
以上,就是Question 2的正解所在,Android 程序入口 ActivityThread.main()
中调用 Looper.prepareMainLooper();
为主线程建立了 Looper
,因此咱们的主线程能够直接建立 Handler
。
Looper.loop();
Looper 肉夹馍
写法,这个方法干啥用的,后面再说。OK,到这里,Handler
,Looper
,MessageQueue
都已经准备就绪,图一的流程差很少能够跑起来了。咱们总结一下重点:
Handler
会检测当前线程 Looper
是否存在,没有则会抛出异常。Handler
以前必须先调用 Looper.Prepare()
建立 Looper
。Looper
能够理解为当前线程的消息调度者,负责消息分发。和当前线程的关联绑定经过 sThreadLocal.set(new Looper(quitAllowed));
实现。Looper
和当前线程是一对一的关系, Looper.Prepare()
方法中有进行校验,重复建立 Looper
绑定会抛出异常。Looper
和 MessagQueue
也是一对一的关系,构造 Looper
对象时会为其建立一个对应的 MessagQueue
对象。Handler
消息发送实际上是一个消息入队的过程,调用 Handler
相应的发送消息方法将当前消息添加到 MessageQueue
消息队列,最终由 Looper
负责进行消息分发。
Handler
为咱们提供了一系列的消息发送方法:
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what){
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
复制代码
发送消息的方法还有不少就不一一列出来了,全部发送消息的方法最终都是调用到 enqueueMessage
方法中:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码
该方法实现最终调用的是queue.enqueueMessage(msg,uptimeMillis)
方法即 MessageQueue
添加消息到消息队列中。
前面提到 Handler
全部发送的消息最终都经过 enqueueMessage
加入到 MessageQueue
消息队列中,因此消息的存储与管理由 MessageQueue
来负责。
消息入队由 enqueueMessage(msg,uptimeMillis)
方法负责:
boolean enqueueMessage(Message msg, long when) {
//验证Message 是否有target,这里的target就是对应的handler
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 {
//将消息按时间顺序插入到MessageQueue。通常地,不须要唤醒事件队列,除非
//消息队头存在barrier,而且同时Message是队列中最先的异步消息。
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;
}
复制代码
消息入队顺序是按照 Message
触发时间 long when
入队的,有新消息加入时,会循环遍历当前消息队列,对比消息触发时间,直到找到当前消息合适的插入位置,以此来保证全部消息的触发时间顺序。
MessageQueue
的 next
方法负责从消息队列中取出一条消息。
Message next() {
.......
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 尝试检索下一条消息。若是找到,则返回。
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
////当消息Handler为空时,查询MessageQueue中的下一条异步消息msg,则退出循环。
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
//当异步消息触发时间大于当前时间,则设置下一次轮询的超时时长
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 获取一条消息,并返回
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg; //成功地获取MessageQueue中的下一条即将要执行的消息
}
} else {
//没有消息
nextPollTimeoutMillis = -1;
}
//消息正在退出,返回null
if (mQuitting) {
dispose();
return null;
}
}
//IdleHandler相关内容
.......
}
}
复制代码
有人( Handler
)在子线程把消息发到我(MessageQueue
)这里来了,我负责把消息存起来,而且告诉你们存储(enqueueMessage
)消息和提取(next
)消息的方法 ,那么谁来负责把我这里的消息分发出去而且告诉主线程的人(Handler
)呢?
干这个活的就是咱们的 Looper
了!
还记得大明湖畔的的 Looper 肉夹馍
么!
如今咱们就来看一下 Looper 肉夹馍
之 Looper.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;
......
//开启死循环
for (;;) {
//循环遍历,不断从MessagQueue中获取Messag
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
try {
//分发消息到对应的msg.target
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
......
msg.recycleUnchecked();
}
}
复制代码
源码解析: 调用 loop()
方法后,方法内部会开启一个死循环,经过调用 MessageQueue
消息出栈方法 next()
获取 Message
,而后调用 msg.target.dispatchMessage(msg);
将消息分发到 Message
对应的 target
。
msg.target
就是发送该消息的Handler
对象。
loop()
方法中调用 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;
}
}
//回调到 Handler 的 handleMessage 方法
handleMessage(msg);
}
}
复制代码
源码解析:
dispatchMessage(msg)
方法内部调用了 handleMessage(msg)
方法,这个方法用过 Handler
的应该都很是熟悉了,它是 Handler
的一个空实现方法,通常在建立 Handler
的线程重写此方法,就能够回调到子线程发出的消息了。
/** * Subclasses must implement this to receive messages. */
public void handleMessage(Message msg) {
}
复制代码
Looper 肉夹馍
职责:
Looper
对象,并关联当前线程。Handler
对象。MessageQueue
消息队列中取消息,取到后分发给 Message
对应的 target
。整个消息从子线程流转到主线程流程:
Looper肉夹馍
。Handler
相应发送消息方法,最终经过 enqueueMessage
将消息加入消息队列 MessageQueue
。Looper
循环从 MessageQueue
获取到消息,调用当前消息对应的 target
的 dispatchMessage(msg)
方法。dispatchMessage(msg)
回调 handleMessage(msg)
方法,消息即从子线程流转到重写 handleMessage(msg)
方法所在线程。Android 主线程默认会使用 Looper 肉夹馍
(详见 ActivityThread.main()
),所以 Android 主线程中只需建立 Handler
对象便可。
主线程 Looper
不能够退出,所以 ActivityThread.main()
方法中调用的 Looper.prepareMainLooper();
方法中 prepare(false);
传参是 false
。 若是强行退出主线程 Looper
会抛出如下异常:
Caused by: java.lang.IllegalStateException: Main thread not allowed to quit.
at android.os.MessageQueue.quit(MessageQueue.java:415)
at android.os.Looper.quit(Looper.java:240)
复制代码
缘由是:Android App 主进程依赖消息机制驱动,主线程消息循环退出了,那么 App 也会退出。
Looper
中开启了一个死循环来从 MessageQueue
中取消息,并且没有单独开线程,为何不会形成 Android 主线程卡死 ANR?