handler的使用都不陌生,这也是面试的重点,在app开发中我们经常使用handler处理异步更新,线程切换。但是我们使用的java层的handler只是一个壳子,总控制和核心逻辑是在native层。 先看下子线程中的handler的使用套路,主线程的looper在activitythread中已经帮我们处理好了。我们就看下子线程的
new Thread(new Runnable() {
public void run() {
// step1 调用prepare
Looper.prepare();
// 构造handler
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Toast.makeText(getApplicationContext(), "handler msg", Toast.LENGTH_LONG).show();
}
};
// step3 发送消息
handler.sendEmptyMessage(1);
// step2 启动循环
Looper.loop();
};
}).start();
一般就是这个套路。下面我们分别看下各方法实现
(时序图中的androidLooper表示java层的Looper.java; nativeLooper表示system/core/libutils/Looper.cpp文件,用来区分)
step1 Looper.prepare()时序图
高清图:www.plantuml.com/plantuml/pn…
从上图可以看出,java层prepare执行之后,将native层的整个消息体系都建立了。包含了native层的Looper\epoll监听。
大致关系:
看下nativeLooper 的rebuildEpollLocked方法
关于epoll介绍网上很多,我们只需要了解下面的api就可以。
Epoll 是一种高效的管理socket的模型,相对于select和poll来说具有更高的效率和易用性。传统的select以及poll的效率会因为 socket数量的线形递增而导致呈二次乃至三次方的下降,而epoll的性能不会随socket数量增加而下降。标准的linux-2.4.20内核不支持epoll,需要打patch。本文主要从linux-2.4.32和linux-2.6.10两个内核版本介绍epoll。
二、 Epoll的使用
epoll用到的所有函数都是在头文件sys/epoll.h中声明的,下面简要说明所用到的数据结构和函数:
所用到的数据结构
typedef union epoll_data {
void ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;
struct epoll_event {
__uint32_t events; / Epoll events /
epoll_data_t data; / User data variable /
};
结构体epoll_event 被用于注册所感兴趣的事件和回传所发生待处理的事件,其中epoll_data 联合体用来保存触发事件的某个文件描述符相关的数据,例如一个client连接到服务器,服务器通过调用accept函数可以得到于这个client对应的socket文件描述符,可以把这文件描述符赋给epoll_data的fd字段以便后面的读写操作在这个文件描述符上进行。
epoll_event 结构体的events字段是表示感兴趣的事件和被触发的事件可能的取值为:
EPOLLIN :表示对应的文件描述符可以读;重要
EPOLLOUT:表示对应的文件描述符可以写;重要
EPOLLPRI:表示对应的文件描述符有紧急的数据可读
EPOLLERR:表示对应的文件描述符发生错误;重要
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET:表示对应的文件描述符设定为edge模式;
所用到的函数:
1、epoll_create函数
函数声明:int epoll_create(int size)
该函数生成一个epoll专用的文件描述符,其中的参数是指定生成描述符的最大范围。
2、epoll_ctl函数
函数声明:int epoll_ctl(int epfd, int op, int fd, struct epoll_event event)
该函数用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。
参数:epfd:由 epoll_create 生成的epoll专用的文件描述符;
op:要进行的操作例如注册事件,可能的取值
EPOLL_CTL_ADD 注册、
EPOLL_CTL_MOD 修改、
EPOLL_CTL_DEL 删除
fd:关联的文件描述符;
event:指向epoll_event的指针;
如果调用成功返回0,不成功返回-1
3、epoll_wait函数
函数声明:int epoll_wait(int epfd,struct epoll_event events,int maxevents,int timeout)
该函数用于轮询I/O事件的发生;
参数:
epfd:由epoll_create 生成的epoll专用的文件描述符;
epoll_event:用于回传代处理事件的数组;
maxevents:每次能处理的事件数;
timeout:-1会导致epoll_wait无限期阻塞,而指定的超时时间等于零导致epoll_wait立即返回,即 使没有可用事件。
返回值:成功时,返回为请求的IO准备就绪的文件描述符的数目;如果在请求的超时毫秒内没有文件描述符准备就绪,则返回零;发生错误时,返回-1并正确设置errno。
void Looper::rebuildEpollLocked() {
// 重置epoll
......
// Allocate the new epoll instance and register the wake pipe.
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
// EPOLLIN可读类型
eventItem.events = EPOLLIN;
// 设置epoll_event中的data的文件描述符是mWakeEventFd
eventItem.data.fd = mWakeEventFd.get();
// 注册mWakeEventFd描述符的事件到epoll,事件由eventItem表示,EPOLLIN(可读)
int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &eventItem);
// 也注册监听其他的事件
for (size_t i = 0; i < mRequests.size(); i++) {
const Request& request = mRequests.valueAt(i);
struct epoll_event eventItem;
request.initEventItem(&eventItem);
int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);
......
}
}
到这里java层和native层的消息体系就构建成功了。
step2 Looper.loop()启动循环
public static void loop() {
final Looper me = myLooper();
// 死循环
for (;;) {
// 获取消息对象
Message msg = queue.next(); // might block
......
try {
msg.target.dispatchMessage(msg);
}
......
}
}
MessageQueue#next()
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
// nextPollTimeoutMillis不等于0的话 都会去执行一下binder任务
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 调用nativePollOnce
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 如果消息链表不是空的并且遇到了消息屏障,找异步消息
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 消息执行的时间还没到
// Next message is not ready. Set a timeout to wake up when it is ready.
// 设置睡眠时间为msg.when - now
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//消息的执行时间已经到了,立即返回这个消息执行
// Got a message.
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;
}
} else {
// 消息链表是空的,设置nextPollTimeoutMillis = -1 表示无限阻塞直到被唤醒
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// 当没有消息的时候,处理空闲消息IdleHandler
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
// 设置nextPollTimeoutMillis = 0不能睡眠了,因为在处理空闲消息的时候可能有新的消息被插入,所以要立即再进入循环查找可用的消息
nextPollTimeoutMillis = 0;
}
}
这段逻辑其实很简单,就是找消息链表中的可用消息,如果找到了就返回给looper去执行,找不到的话要分情况:
- 消息链表是空的,那么就设置nextPollTimeoutMillis = -1,表示要睡眠直到被唤醒,并处理空闲消息,空闲消息执行后再设置nextPollTimeoutMillis = 0,因为这个过程可能有新的消息被插入了,所以要再进入循环查找可用消息
- 链表不为空,那么就判断消息的执行时间和当前系统时间
- 如果到执行时间的话,就返回这个消息给looper进行后续的执行;
- 如果没到执行时间那么设置nextPollTimeoutMillis = msg.when - now,睡眠这么久,来 保证消息的正确执行; 整体流程梳理完了,那么要看看nativePollOnce是怎么实现的?
流程图:
高清图:www.plantuml.com/plantuml/pn…
system/core/libutils/Looper.cpp
int Looper::pollInner(int timeoutMillis) {
// Adjust the timeout based on when the next message is due.
if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);
if (messageTimeoutMillis >= 0
&& (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {
timeoutMillis = messageTimeoutMillis;
}
}
// Poll.
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
// We are about to idle.
mPolling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
// 睡眠timeoutMillis,这时间是java层传进来的
// eventItems保存了所有有变动的文件, eventCount表示有变动的文件个数,所以epoll效率
// 比较高,不用遍历所有的数据了。
int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// No longer idling.
mPolling = false;
// Acquire lock.
mLock.lock();
// Rebuild epoll set if needed.
if (mEpollRebuildRequired) {
mEpollRebuildRequired = false;
rebuildEpollLocked();
goto Done;
}
// Check for poll error.发生错误了
if (eventCount < 0) {
if (errno == EINTR) {
goto Done;
}
result = POLL_ERROR;
goto Done;
}
// Check for poll timeout.
// 没有就绪的文件
if (eventCount == 0) {
result = POLL_TIMEOUT;
goto Done;
}
// 遍历所有的事件
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeEventFd.get()) {
// 如果是前面mWakeEventFd对应的文件,并且事件是EPOLLIN
if (epollEvents & EPOLLIN) {
// 把管道里面的数据读出来,清空管道
awoken();
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on wake event fd.", epollEvents);
}
} else {
// 其他类型的文件
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex >= 0) {
// 通过addFd方法添加的
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;
// 加入到mResponses,但并没有马上执行
pushResponse(events, mRequests.valueAt(requestIndex));
} else {
ALOGW("Ignoring unexpected epoll events 0x%x on fd %d that is "
"no longer registered.", epollEvents, fd);
}
}
}
Done: ;
//开始处理消息
// Invoke pending message callbacks.
mNextMessageUptime = LLONG_MAX;
// 先处理通过sendMessageXxx 添加的native消息
while (mMessageEnvelopes.size() != 0) {
nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
if (messageEnvelope.uptime <= now) {
// Remove the envelope from the list.
// We keep a strong reference to the handler until the call to handleMessage
// finishes. Then we drop it so that the handler can be deleted *before*
// we reacquire our lock.
{ // obtain handler
sp<MessageHandler> handler = messageEnvelope.handler;
Message message = messageEnvelope.message;
mMessageEnvelopes.removeAt(0);
mSendingMessage = true;
mLock.unlock();
// 调用handler处理消息
handler->handleMessage(message);
} // release handler
mLock.lock();
mSendingMessage = false;
result = POLL_CALLBACK;
} else {
// The last message left at the head of the queue determines the next wakeup time.
mNextMessageUptime = messageEnvelope.uptime;
break;
}
}
// Release lock.
mLock.unlock();
//再处理mResponses中的消息
// Invoke all response callbacks.
for (size_t i = 0; i < mResponses.size(); i++) {
Response& response = mResponses.editItemAt(i);
if (response.request.ident == POLL_CALLBACK) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
int callbackResult = response.request.callback->handleEvent(fd, events, data);
// callbackResult==0 移除Fd
if (callbackResult == 0) {
removeFd(fd, response.request.seq);
}
// Clear the callback reference in the response structure promptly because we
// will not clear the response vector itself until the next poll.
// 移除callback
response.request.callback.clear();
result = POLL_CALLBACK;
}
}
return result;
}
epoll_wait睡眠timeoutMillis时间,或者其他线程往消息队列中添加消息因为添加消息会往mWakeEventFd写入数据,会唤醒epoll。可以看到都会走到Done的代码,处理消息是先处理native的消息,再处理request类型的消息。