带着以下几个问题去看Handler源码
- 1、Handler与线程的关系;
- 2、Handler、Looper、MessageQueue的关系;
- 3、Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
- 4、Activity的生命周期这些方法这些都是在主线程里执行的吧,那这些生命周期方法是怎么实现在死循环体外能够执行起来的?
- 5、postDelay时, 任务是如何被添加到消息队列中的?
打算按下面顺序进行分析Looper体系:
- 1、创建轮询器new Looper
- 2、创建消息队列new MessageQueue
- 3、启动轮询器进行消息查询Looper.loop
- 4、通过Handler.post/sendMessage向消息队列中添加msg
一、轮询器Looper
1.1 ActivityThread.main主线程Looper的创建入口
static Handler sMainThreadHandler;
// ActivityThread.main是主程序的入口, 在这里进行Looper与Message的初始化操作;
public static void main(String[] args) {
// 创建轮询器Looper
Looper.prepareMainLooper();
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// 启动轮询器
Looper.loop();
}
小结:
main里面要计划完成三件事: 创建轮询器Looper、创建Handler、启动轮询器Looper.
1.2 Looper.prepareMainLooper创建轮询器Looper与消息列队
public static void prepareMainLooper() {
// 创建Looper, 此时Looper关联的是UI线程
prepare(false);
synchronized (Looper.class) {
// 这里限制了一个线程只能有一个Looper
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
// 对sMainLooper进行赋值
sMainLooper = myLooper();
}
}
private static void prepare(boolean quitAllowed) {
// Looper与Thread是1:1的关系
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 初始化Looper, 将Looper通过ThreadLocal与当前线程进行绑定
sThreadLocal.set(new Looper(quitAllowed));
}
public static void prepare() {
prepare(true);
}
private Looper(boolean quitAllowed) {
// 一个线程对应一个Looper, Looper初始化时进行了MessageQueue的初始化,
// 所以Thread、Looper、MessageQueue三者关系为1:1:1
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
小结:
上面流程完成之后, 代码层面规定了Thread、Looper、MessageQueue三者的关系是1:1:1, 但是还有一个疑问是quitAllowed这个变量, prepareMainLooper()与prepare()分别传入的false/true
, 这个变量对Looper体系有什么影响?
二、消息队列MessageQueue
2.1 MessageQueue构造函数
private native static long nativeInit();
// java层的MessageQueue持有native层的NativeMessageQueue的引用;
private long mPtr;
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
// 触发native层方法, 获取native层的NativeMessageQueue的引用, 使其指向mPtr;
mPtr = nativeInit();
}
http://androidxref.com/7.1.1_r6/xref/frameworks/base/core/jni/android_os_MessageQueue.cpp;
2.2 MessageQueue.nativeInit
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
nativeMessageQueue->incStrong(env);
return reinterpret_cast(nativeMessageQueue);
}
小结:
native创建NativeMessageQueue对象, 并将该对象的引用返回给java层MessageQueue的mPtr变量, 从而将Java层MessageQueue与NativeNativeMessageQueue产生了关联, 为后续Looper没有消息到来时进入阻塞状态打下基础
2.3 NativeMessageQueue构造函数
NativeMessageQueue::NativeMessageQueue() :
mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
// 创建Native层的Looper, 然后将Native层的Looper与当前线程进行绑定
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
小结:
创建Native层的Looper, 将Looper对当前线程进行绑定, 这里也采用的是单例的模式, 所以Native层Looper、NativeMessageQueue、三者的关系也是1:1:1
三、启动轮询器
3.1 Looper.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 (;;) {
// 轮询器从消息队列中获取消息, 然后执行该消息.
Message msg = queue.next();
// 如果msg为null, 也就是说消息队列中没有消息了, 那么轮询器退出当前轮询操作, 加入当前msg为null,
// 然后return退出当前轮询操作, 那么别的线程再向往消息队列中添加信息时岂不是每次都需要调用loop()?
// 其实通过下文中的queue.next()发现并不会出现msg = null的情况
if (msg == null) {
return;
}
// 取消消息以后, 然后执行该消息
msg.target.dispatchMessage(msg);
msg.recycleUnchecked();
}
}
小结:
1、轮询器looper的轮询操作是在一个while(true)循环中执行的, 从代码层面看, 退出while(true)的条件是msg = null, 然后考虑一个问题, msg到底会不会存在=null的情况?
2、取出消息, msg不为null的情况下, 执行该msg.
3.2 执行消息msg.target.dispatchMessage
// 1. post方式时callback == Runnable != null, 触发handlerCallback();
// 2. sendMessage方式时callback == null, 触发handleMessage;
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
小结:
从消息队列中取出消息是进行消息分发处理, 这里分发的依据是Handler发送消息的方式, Handler发送消息主要有两种方式: post与sendMessage, 如果是post方式, 则执行handleCallback方法, 如果是sendMessage方式, 则执行handleMessage方法
3.3 从消息队列中读取消息MessageQueue.next
private native void nativePollOnce(long ptr, int timeoutMillis);
Message next() {
int pendingIdleHandlerCount = -1;
// nextPollTimeoutMillis默认为0;
int nextPollTimeoutMillis = 0;
for (;;) {
// 记住这个方法是一个native的方法, 他在native层到底做了什么操作?
// 通过对下面代码的分析可知, nextPollTimeoutMillis有三种值:
// (1) nextPollTimeoutMillis = 0: 消息队列中由消息;
// (2) nextPollTimeoutMillis = Math.min(msg.when - now, Integer.MAX_VALUE): 延时消息;
// (3) nextPollTimeoutMillis = -1: 当前消息队列中没有消息;
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 1.通过查看Message的源码, Message是一个链表结构;
// 2.这里先记住结论, Handler.enqueueMessage时会将Message与Handler进行绑定,
// 所以正常情况下msg.target != null;
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
// 如果now < msg.when, 说明当前消息为延时消息, 然后跳过该if语句继续向下执行
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//
mBlocked = false;
// 从链表中FIFO的方式依次取出消息, 然后从链表中删除该元素
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
// 取出msg返回msg
return msg;
}
} else {
// 当msg = null时, nextPollTimeoutMillis = -1.
nextPollTimeoutMillis = -1;
}
// 执行接下来的代码有两种情况:
// 1. msg != null, 但是now < msg.when, 也就是说当前消息为延时消息,
// 此时nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE)
// 2. msg == null, 也就是说此时消息队列中没有消息, 此时nextPollTimeoutMillis = -1
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// 再次执行for循环操作
mBlocked = true;
continue;
}
}
nextPollTimeoutMillis = 0;
}
}
小结:
重点在nextPollTimeoutMillis取值上面.
1、nextPollTimeoutMillis = 0: 消息队列中由消息;
2、nextPollTimeoutMillis = Math.min(msg.when - now, Integer.MAX_VALUE): 延时消息;
3、nextPollTimeoutMillis = -1: 当前消息队列中没有消息;
3.4 MessageQueue.nativePollOnce
MessageQueue->nativePollOnce:
static void android_os_MessageQueue_nativePollOnce(...jlong ptr, jint timeoutMillis) {
// 结合上文可知ptr指向的就是NativeMessageQueue对象
NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
NativeMessageQueue->pollOnce():
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mLooper->pollOnce(timeoutMillis);
}
3.5 Looper.pollOnce
Looper->pollOnce(...):
// timeoutMillis为我们在java中传入的nextPollTimeoutMillis变量;
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
while (mResponseIndex < mResponses.size()) {...}
if (result != 0) {...}
result = pollInner(timeoutMillis);
}
}
int Looper::pollInner(int timeoutMillis) {
// 只观察timeoutMillis引用的地方;
// 由上文Native层的Looper初始化知道, mNextMessageUptime = LLONG_MAX;
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {...}
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
mPolling = true;
// 1. 当timeoutMillis = -1时, 由epoll_wait定义可知该方法会一直阻塞;
// 2. 如果timeoutMillis = 0继续向下执行, 并获取事件个数;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
// epoll_wait是一个Linux函数, 这里只记住结论.
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
/**
* 当eventCount > 0 时, 遍历该eventItem, 并判断当前文件描述符是否为
* mWakeEventFd, 如果是mWakeEventFd则调用awoken()唤醒, 否则则将该文件描述符
* 添加到mResponses中去;
*/
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd) {
if (epollEvents & EPOLLIN) {
awoken();
}
} else {
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
int events = 0;
if (epollEvents & EPOLLIN) events |= EVENT_INPUT;
if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;
if (epollEvents & EPOLLERR) events |= EVENT_ERROR;
if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;
pushResponse(events, mRequests.valueAt(requestIndex));
}
else {...}
}
}
return result;
}
3.6 epoll_wait
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout):
小结:
1、timeout = 0: 立即返回;
2、timeout = -1: 永久阻塞当前线程;
3、timeout > 0: 当前线程阻塞timeout时间后继续向下执行;
总结:
如果消息队列为空, java层nextPollTimeoutMillis = -1, 对应native层timeout = -1, 此时当前线程会进入阻塞状态, 也就是Looper所在线程会被挂起.
四、Handler
4.1 Handler构造函数
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
// 1. 结合前文其实可知, Looper的获取使用了ThreadLocal, 也就是说一个线程只能有一个Looper;
// 2. 但是Handler初始化时并没有做相关限制, 所以Handler与Looper的关系是多:1的关系;
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
}
小结:
初始化Handler之前, 需要先创建一个轮询器Looper.
4.2 提交消息Handler.post
public final boolean post(Runnable r) {
// 1. 将Runnable封装进Message中, 然后将Message发送到MessageQueue;
// 2. 如果是发送延时消息, 第二个参数delayMillis是不为0的;
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
// 初始化Message, 将r指向Message.callback;
Message m = Message.obtain();
m.callback = r;
return m;
}
public final class Message {
public static Message obtain() {
return new Message();
}
}
4.3 遍历消息链表, 选择合适的位置插入msgHandler.sendMessageDelayed
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
// 将当前时间与延时时间进行叠加获取消息处理的时间.
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
// msg进行入队操作;
return queue.enqueueMessage(msg, uptimeMillis);
}
4.4 其他线程发送消息唤醒MessageQueueMessageQueue.enqueueMessage
boolean enqueueMessage(Message msg, long when) {
...
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 1. 进入if的三种情况:
// (1) 当前消息队列中没有消息;
// (2) 提前执行任务, 也就是说SystemClock.uptimeMillis() + delayMillis = 0?
// (3) when < p.when, 新插入的消息在当前消息之前执行, 也就是会插入到表头的位置;
// 2. 结合上文MessageQueue.next可知, 当消息队列为空时, Looper所在线程会在nativePollOnce处进入
// 挂起状态, 同时mBlocked被置为true, 知道外部线程通过I/O方式唤醒Looper线程.
if (p == null || when == 0 || when < p.when) {
msg.next = p;
// mMessages相当于是链表的表头, 此时将当前消息置为表头的位置
mMessages = msg;
needWake = mBlocked;
} else {
// 此时很显然p.target == null为false, neekWake = false;
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 关于开头提出的问题, 延时消息的插入是在插入时进行延时, 还是在取出时进行延时?
// 遍历链表按执行时间的先后顺序进行插入msg.
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;
}
// 1. 能进入if语句内部的前提条件是needWake = true, 而needWake唯一被赋值就是needWake = mBlocked;
// 2. mBlocked只在MessageQueue.next()中被赋值.而结合上文又知道当没有消息被添加到MessageQueue队列中时,
// 也就是Message链表为空时, MessageQueue.next()会被一直阻塞在nativePollOnce(...), 而且mBlocked = true,
// 当有消息被添加到消息队列时, 是一定会执行下面if(...)语句;
if (needWake) {
// 1. 通过模块<3.3>可知, 当没有消息时, 当前线程会在native层通过触发epoll_wait方法并传入
// -1将当前线程处于阻塞状态;
// 2. 然后在有消息到来时, 通过nativeWake方法欢喜线程;
nativeWake(mPtr);
}
return true;
}
}
private native static void nativeWake(long ptr);
小结:
1、当没有消息到来时, MessageQueue.next()会被一直阻塞在nativePollOnce(...)处, 并且此时mBlock = true;
2、当调用Handler.post或者Handler.sendMessage时, MessageQueue.enqueueMessage(...)会触发nativeWake(...)的执行;
3、插入消息时, 遍历当前消息链表, 插入msg时满足的条件是when从小到大的顺序, 也就是msg执行的先后顺序进行msg的插入操作;
4.5 android_os_MessageQueue.nativeWake
MessageQueue --->
static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast(ptr);
nativeMessageQueue->wake();
}
void NativeMessageQueue::wake() {
mLooper->wake();
}
4.6 Looper.wake
Looper --->
void Looper::wake() {
uint64_t inc = 1;
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
}
重点在这里的write(...)部分, 注意前文的epoll_wait(...)传入-1时会阻塞当前线程, 这里调用write(...)通过唤醒当前线程, pollOnce(...)会继续向下执行, 最终走到awoken方法;
Looper.cpp --->
void Looper::awoken() {
uint64_t counter;
TEMP_FAILURE_RETRY(read(mWakeEventFd, &counter, sizeof(uint64_t)));
}
从管道内读入数据, 即native层不会再阻塞在pollOnce(...), 所以java层的MessageQueue.next()也就不会再阻塞在nativePollOnce(ptr, nextPollTimeoutMillis)处, MessageQueue.next()继续向下执行由执行到了return msg处, 然后根据我们Handler.post还是Handler.sendMsg而选择是调用Handler.handlerCallback(...)还是Handler.handleMessage(...);
4.7 Handler.dispatchMessage
// 1. post方式时callback == Runnable != null, 触发handlerCallback();
// 2. sendMessage方式时callback == null, 触发handleMessage;
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
总结一下:
1、关于IO多路复用, epoll以及pipe并不是很了解, 导致分析经常卡壳, 到现在也不能说清楚;
多路复用_epoll:
1、概念:
epoll使用一个文件描述符管理多个描述符, 将用户关系的文件描述符的事件存放到内核的一个事件表中, 这样在用户空间和内核空间的copy只需要一次;
2、epoll接口:
#include
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
(1) int epoll_create(int size):
创建一个epoll句柄, size用来告诉内核这个监听的数目一共有多大. 当创建好epoll句柄后, 它就是会占用一个fd值, 在使用完epoll之后, 必须调用close()关闭, 否则就可能导致fd被耗尽;
(2) int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event):
epoll的事件注册函数; 第一个参数是epoll_create()的返回值, 第二个参数表示动作, 用三个宏来表示:
EPOLL_CTL_ADD: 注册新的fd到epfd中;
EPOLL_CTL_MOD: 修改已经注册的fd的监听事件;
EPOLL_CTL_DEL: 从epfd中删除一个fd;
第三个参数是需要监听的fd, 第四个参数是告诉内核需要监听什么事件;
struct epoll_event {
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};
events可以是以下几个宏的集合:
EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET:将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里
(3) int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout):
等待事件的产生, 参数events用来从内核得到事件的集合, maxevents告诉内核这个events有多大, 参数timeout是超时时间(毫秒, 0会立即返回, -1将会阻塞). 该函数返回需要处理的事件数目, 如返回0表示已超时;
参考文章:
https://www.cnblogs.com/Anker/p/3263780.html