您的位置:首页 > 产品设计 > UI/UE

从HandlerThread源码理清handler、looper与messageQueue之间的关系

2017-04-05 17:49 573 查看
前端时间看到一篇关于HandlerThread 的介绍博文,感觉写的不错;再由于近段时间遇到的一个面试题:looper是如何与线程绑定到一起的?当时回答的很不流畅,因此回去之后又跟着源码分析了一边这三者之间的关系,并以此记录下来(sdk以Android-24为例)。

首先看一段简短的代码

HandlerThread handlerThread = new HandlerThread("hai");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
handler.sendEmptyMessage(1);
代码的功能就是一个系统已经定义好的一个thread,new出来之后启动,然后拿到这个thread 的handler,通过handler发送消息,thread里的looper接收到消息后把消息交给handler处理。ok!代码片段解释到此结束,下面开始分析HandlerThread是如何实现Android系统的handler-looper消息循环机制。

public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;

public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
由上看出HandlerThread继承Thread,即它就是一个自定义的Thread。然后handlerThread.start()后线程启动,执行run方法。其实它和自new线程然后在run方法中调用Looper.prepare,Looper.loop 的实现是一个道理

@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
且看Looper.prepare(),sThreadLocal是Looper类加载时就初始化的一个对象

public static void prepare() {
prepare(true);
}

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));
}
由sThreadLocal.get()进去
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
由于新new出来的线程的threadLocals没初始化,所以getMap(t)返回空,因而执行setInitialValue(),

private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}


void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}


由于getMap(t)==null,因此当前Thread会为自己的threadLocals作一个初始化一个ThreadLocalMap,键就是当前的ThreadLocal,firstValue为null。ThreadLocalMap的作用类似于Map存放键值对。
由于sThreadLocal.get()得到的值为空,因此会sThreadLocal.set(new Looper(quitAllowed)),

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
当前线程中new的Looper中初始化了一个MessageQueue和保存了对当前线程的引用。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
set(T value)的作用就是把new出来的Looper保存到当前Thread 的 threadLocals变量中。

再通过new Handler(handlerThread.getLooper())就得到了和当前线程Looper关联的Handler。

到此,与此Thread关联的Looper、messageQueue、Handler就此登场了。看看他们两的类图,



下面上重点:looper是如何轮询的?看 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;

// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();

for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
Looper通过一个for循环不断的调用queue.next()取message,(这里先说明下,queue.next()不会返回空,除非线程正在退出)

Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}

int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}

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.
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;
}


从for开始看,它也是一个for循环,每次循环都会nativePollOnce一次(它的作用暂时不知),然后找寻msg,找到则返回,如果messageQueue为空则调用next()方法的线程会一直在此处for循环。一个new的新线程MessageQueue中 mMessages是null,所以线程会一直循环,直到Handler send一个message后,mMessages才不会为空。

一句话总结就是:Thread一直在调用Looper的for循环,而Looper的for循环则一直在调用Messagequeue的for循环,即Thread一直在调用MessageQueue的for循环。
最后说明下UI线程,它也是通过调用Looper.prepareMainLooper()和Looper.looper来实现消息轮询的,具体可以看看ActivityThread的main方法实现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐