版权声明
版权声明:本文为博主原创文章,转载请注明出处+地址
什么是Handler消息机制
Android内部的Handler消息机制,是将子线程处理的结果,通过Handler异步回调给主线程,让主线程去进行操作的一个机制。
Handler消息机制的工作流程图
Handler的源码解析
Handler使用案例
首先,给大家简单的展示下Handler的使用流程代码
/**
* Function:简述Handler使用方式
*
* @author wanzi Created on 2019/4/28
*/
public class HandlerActivity extends AppCompatActivity {
private MyHandler mHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mHandler = new MyHandler();
Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
@Override
public void run() {
Message msg = new Message();
msg.what = 1;
mHandler.sendMessage(msg);
}
}, 5000, TimeUnit.MILLISECONDS);
}
private static class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1) {
Log.d("HandlerTestActivity", "Handler发来贺电");
}
}
}
}
Handler是如何将消息加入到消息队列?
分析源码,我们首先要找到切入点,mHandler.sendMessage(msg),代码从这个函数开始调用的handler,那么继续往下追踪,我们来研究下,在发送消息之后,Handler干了什么?
Handler.java
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);
}
在经过层层调用之后,我们发现调用到了enqueueMessage方法,根据字面意思,就是加入消息队列,那我们继续看里面的实现
Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
- 将当前的handler对象赋值给了msg.target
- 调用了messagequeue的enqueueMessage方法
MessageQueue.class
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
...
if (p == null || when == 0 || when < p.when) {
...
} else {
...
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
...
}
return true;
}
到此为止,msg算是终于成功的加入到消息队列当中,当然MessageQueue.enqueueMessage的内容远不止这么简单,里面包含一些异常处理,唤醒机制等等,单独写一篇文章详细的讲解。
Handler如何从消息队列获取消息?
主线程的Looper如何创建?
说到获取消息,我们还得从Looper说起,大家都知道,在主线程中创建的Handler对象,是自带Looper对象的,那么主线程中的Looper是从哪里创建的呢?
我们知道,程序启动开始的入口在ActivityThread.class的main函数,我们来看看代码
ActivityThread.class
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
Looper.loop();
...
}
简化之后,我们可以看出,在main函数中,有两处和Looper相关的代码,那么我们分别来看下
** Looper.class**
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
- prepareMainLooper最终调用到的是prepare函数
- prepare的quitAllowed参数值为false,意味着在主线程中,不允许退出
- 在prepare函数中,我们new了一个Looper对象,放在了一个ThreadLocal的对象中保存
- sMainLooper 是将sThreadLocal中保存的值取出,赋值给它
ThreadLocal的简单介绍
在整个Looper的创建以及获取的过程,其实主要跟ThreadLocal的set,get函数相关,那么我们就来看看这两个函数的源码
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
- set函数虽然只需要传value值进来,但是其实它是获取的当前线程作为key值,将其保存,这也就说明为什么一个线程只对应来一个looper,因为他的存储的数据结构只支持一对一的关系。
- get函数也是通过以当前线程作为key将对应的entry取出,返回给调用方
那么以上就是mainLooper的创建的主要流程
主线程的Looper如何开启消息轮询的呢?
大家回顾下前面ActivityThread.main方法,我们还有一行代码没有分析,那就是Looper.loop,根据字面意思我们也能猜出,这是开始进行循环,话不多说,上代码
Looper.class
public static void loop() {
final Looper me = myLooper();
...
final MessageQueue queue = me.mQueue;
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
...
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
}
这里我们同样省略了很多源码,只分析主要流程
- 通过myLooper()获取到当前线程的looper对象,上面也带大家一起分析过源码了,有不清楚的可以回过去看看
- 通过拿到的looper对象去获取到当前的消息队列
- 进入死循环
- 不断的从队列中获取新的消息
- 获取到消息后,msg.target.dispatchMessage(msg)
以上就是Looper开启轮询的整个调用流程。
下面我带大家一起看下几个问题:
- MessageQueue.next是如何拿到message的呢?
MessgeQueue.class
Message next() {
...
for (;;) {
...
synchronized (this) {
...
Message prevMsg = null;
Message msg = mMessages;
...
if (msg != null) {
if (now < msg.when) {
...
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
...
msg.markInUse();
return msg;
}
} else {
...
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
...
}
- 在next函数中也有一个死循环,当获取到我们的msg之后,跳出循环
- next函数,就是从当前的message获取到nextMessage,并将其返回给looper
看完之后,你一定还有很多疑问,为什么要用到死循环?当消息队列为空的时候,next函数会怎么办?等等等等,跟MessageQueue.enqueueMessgae方法一样,next函数的实现大有文章,单独为大家写一篇详细介绍,这里只作简单的流程分析。
- 什么情况下msg为空,跳出整个循环呢?
这一块先跟大家简单的介绍一下,在next函数中,我们发现有return 为 null的情况,取决于一个叫mQuitting的参数,其实就是调用了MessageQueue.quit()方法,将其设置为true。但是在主线程中,大家还记得我们在Looper.prepare传入的false参数吗?它不允许主线程的MessageQueue执行quit方法,直接抛出异常。
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
- msg.target.dispatchMessage(msg)是怎么回调到handler.handleMessage方法的呢
** Message.class **
/*package*/ Handler target;
是的,正如上面我们所预料的,target其实是一个handler的对象,在Handler.enqueueMessage方法中,我们将msg与handler进行了关联,那么在Looper.loop里面,msg.target.dispatchMessage(msg),根据源码可以得知,我们将其回调给了handleMessage进行事件处理。
/**
* 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);
}
}
以上就是针对Handler源码的一个简单的分析,后续会对一些细节化的设计,进行专题讲解,敬请关注。