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 < eventCount; i++) Looper其实很简单