Looper其实很简单
Posted xjz729827161
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Looper其实很简单相关的知识,希望对你有一定的参考价值。
每次提到这个looper,就有点感觉轻视的感觉,不就是个消息列队么。可是当别人问起,又不知道从何讲起,这次来一起捋一捋其中的道道,捋清楚后发现,还是很简单,哈哈。
Looper是一种单个线程中持续处理消息的的方式。
涉及到几个重要的类
Looper
// 消息的调度
MessageQueue
// 消息的存储
Message
//消息本身,可以负载数据,也分为同步消息,异步消息
Handler
//callback, runable, 消息的入口和处理地方
以主线程的Looper为例
创建Looper
ActivityThread.java
public static void main(String[] args) {
Looper.prepareMainLooper();
...
Looper.loop();
}
Looper.java
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) {
//每个线程只能创建一次,每个线程如果调用后都拥有自己的looper
//因为这个原因,衍生出一个类HandlerThread,是一个内部启动了Looper的线程
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
这里需要插入一个知识点ThreadLocal
结束Looper
因为运行比较重要,就把结束放在前面说, 有两种方式quit和quitSafe,区别在于,quit会把队列中所有的消息出队,而quitSafe先只处理消息中大于当前时间的消息
Looper.java
public void quit(){}
public void quitSafe(){}
运行Looper,消息处理
Looper.java
//调用这个开始这个循环. emm..代码精简后就是这么简单。
//整体的思路就是不断的从队列里面去取消息, 取到消息就扔给消息的target进行派发,没有消息则阻塞
looper.loop() {
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
//找到msg对应的handler来处理消息
msg.target.dispatchMessage(msg);
} finally {
}
}
...
}
MessageQueue.java 取数据
让我们康康queue.next里面做了啥
Message next() {
for (;;) {
//这里会真正的进行阻塞,还会处理事件消息,里面是epoll_wait
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;
//msg.target == null的是屏障, 遇到屏障之后只取异步消息
//这里就有一个问题,如果扔了一个屏障进来,不取消就ANR卡死了
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) {
//整个队列的消息其实是以时间戳排序的,如果这个时间没有到
//执行的时间就继续等待,这种时间延后的就是延迟消息是平时使用的handler.postDelay之类的
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
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 {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// 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);
}
//一个闲置处理,没有消息处理的时候,再触发逻辑
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);
}
}
}
}
}
上面的过程就完成了java端的looper。
如果不想去了解IO的派发过程,那么完全可以不用计较nativePollOnce(ptr, nextPollTimeoutMillis)
里面的细节
可以理解为里面就做了一件事
//使用epoll的wait方法
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
队列中扔数据
MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
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);
}
}
唤醒的动作
Looper.cpp
void Looper::wake() {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ wake", this);
#endif
uint64_t inc = 1;
//重点就这一句,向epoll的唤醒文件描述符中,写入一个值
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s",
mWakeEventFd, strerror(errno));
}
}
}
总结下整个流程就是
- 挂起唤醒: 通过epoll监听了一个 wakeFd文件, 如果没有消息的的时候可以调用
epoll_wait
挂起线程,
有消息时向wakeFd中写入一个数值1即可唤醒epoll_wait
的阻塞。 - 消息: 消息分为异步和同步,绑定了一个target(Handler)来处理消息,
如果target为null则是屏障消息, 屏障消息的作用是,所有的后入同步消息都在屏障消息后,除非屏障取消。
这里一般用在invalidate触发的viewRootImpl performTravels中,保证UI刷新的优先级。
异步消息也有一个使用的例子就是:Choreographer
的postCallbackDelayedInternal
里有用到。
里面额外补充一点的就是用到了重用的概念,有一个消息池,
毕竟消息只是一个载体,没必要每次都反复生成对象,建议多使用Message.obtain()
来创建消息 - 消息队列: 入队消息, 按照消息按照时间戳进行排序,如果插入了非异步消息,并且当前已经阻塞了,则进行唤醒动作
- Handler: 如果大家说怎么都没有看到说这个,是因为它其实不重要,message的target指向了一个handler. 然后会调用handler的disptachMessage. 这里也引入了一个知识点,handler的泄露,假设场景Activity创建匿名内部类Handler的实例来处理消息,则引用关系为 MessageQueue -> Message -> Handler -> Activity, 嗯,会泄露
OK , 以上的就有整个队列消息的取值,值入队, 阻塞,唤醒的动作。
当然也有头铁的大哥说,我就要了解下上面你说跳过的nativePollOnce
里面的细节,其实也不复杂, epoll是可以监控多个文件的,除了监听一个给上层用来唤醒的文件外,还可以监听额外的文件。 实际中有用到的就是 监听的事件派发的socket文件的写入
android_os_MessageQueue.cpp
//里面有一个动态注册的,java的方法与native方法的映射
static const JNINativeMethod gMessageQueueMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "()J", (void*)android_os_MessageQueue_nativeInit },
{ "nativeDestroy", "(J)V", (void*)android_os_MessageQueue_nativeDestroy },
{ "nativePollOnce", "(JI)V", (void*)android_os_MessageQueue_nativePollOnce },
{ "nativeWake", "(J)V", (void*)android_os_MessageQueue_nativeWake },
{ "nativeIsPolling", "(J)Z", (void*)android_os_MessageQueue_nativeIsPolling },
{ "nativeSetFileDescriptorEvents", "(JII)V",
(void*)android_os_MessageQueue_nativeSetFileDescriptorEvents },
};
//对应方法为 android_os_MessageQueue_nativePollOnce
//里面又引入了一个类NativeMessageQueue
static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
jlong ptr, jint timeoutMillis) {
NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
}
void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
mPollEnv = env;
mPollObj = pollObj;
mLooper->pollOnce(timeoutMillis);
mPollObj = NULL;
mPollEnv = NULL;
if (mExceptionObj) {
env->Throw(mExceptionObj);
env->DeleteLocalRef(mExceptionObj);
mExceptionObj = NULL;
}
}
Looper.h (core/libutils/include/utils/)
inline int pollOnce(int timeoutMillis) {
return pollOnce(timeoutMillis, NULL, NULL, NULL);
}
Looper.cpp
//对于上层的pollOnce来说,除了timeoutMillis,后面的都NULL
int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
int result = 0;
for (;;) {
//每个request其实是封装的fd的监听
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
int ident = response.request.ident;
if (ident >= 0) {
int fd = response.request.fd;
int events = response.events;
void* data = response.request.data;
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning signalled identifier %d: "
"fd=%d, events=0x%x, data=%p",
this, ident, fd, events, data);
#endif
if (outFd != NULL) *outFd = fd;
if (outEvents != NULL) *outEvents = events;
if (outData != NULL) *outData = data;
return ident;
}
}
if (result != 0) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - returning result %d", this, result);
#endif
if (outFd != NULL) *outFd = 0;
if (outEvents != NULL) *outEvents = 0;
if (outData != NULL) *outData = NULL;
return result;
}
result = pollInner(timeoutMillis);
}
}
int Looper::pollInner(int timeoutMillis) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
#endif
// 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;
}
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - next message in %" PRId64 "ns, adjusted timeout: timeoutMillis=%d",
this, mNextMessageUptime - now, timeoutMillis);
#endif
}
// Poll.
int result = POLL_WAKE;
mResponses.clear();
mResponseIndex = 0;
// We are about to idle.
mPolling = true;
struct epoll_event eventItems[EPOLL_MAX_EVENTS];
//重点来了。 epoll_wait,阻塞并支持超时
int eventCount = epoll_wait(mEpollFd, 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;
}
ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
result = POLL_ERROR;
goto Done;
}
// Check for poll timeout.
if (eventCount == 0) {
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - timeout", this);
#endif
result = POLL_TIMEOUT;
goto Done;
}
// Handle all events.
#if DEBUG_POLL_AND_WAKE
ALOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
#endif
//读取事件
for (int i = 0; i Looper其实很简单
Android Handler处理机制 ( 三 ) ——Handler,Message,Looper,MessageQueue