Android消息循环机制源码深入理解
android消息循环机制源码
前言:
搞android的不懂handler消息循环机制,都不好意思说自己是android工程师。面试的时候一般也都会问这个知识点,但是我相信大多数码农肯定是没有看过相关源码的,顶多也就是网上搜搜,看看别人的文章介绍。学姐不想把那个万能的关系图拿出来讨论。
近来找了一些关于android线程间通信的资料,整理学习了一下,并制作了一个简单的例子。
andriod提供了 handler 和 looper 来满足线程间的通信。例如一个子线程从网络上下载了一副图片,当它下载完成后会发送消息给主线程,这个消息是通过绑定在主线程的handler来传递的。
在android,这里的线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个looper,这个事android的新 概念。我们的主线程(ui线程)就是一个消息循环的线程。针对这种消息循环的机制,我们引入一个新的机制handle,我们有消息循环,就要往消息循环里 面发送相应的消息,自定义消息一般都会有自己对应的处理,消息的发送和清除,消息的的处理,把这些都封装在handle里面,注意handle只是针对那 些有looper的线程,不管是ui线程还是子线程,只要你有looper,我就可以往你的消息队列里面添加东西,并做相应的处理。
但是这里还有一点,就是只要是关于ui相关的东西,就不能放在子线程中,因为子线程是不能操作ui的,只能进行数据、系统等其他非ui的操作。
在android,这里的线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个looper,这个是android的新概念。我们的主线程(ui线程)就是一个消息循环的线程。针对这种消息循环的机制,我们引入一个新的机制handler,我们有消息循环,就要往消息循环里面发送相应的消息,自定义消息一般都会有自己对应的处理,消息的发送和清除,把这些都封装在handler里面,注意handler只是针对那 些有looper的线程,不管是ui线程还是子线程,只要你有looper,我就可以往你的消息队列里面添加东西,并做相应的处理。
但是这里还有一点,就是只要是关于ui相关的东西,就不能放在子线程中,因为子线程是不能操作ui的,只能进行数据、系统等其他非ui的操作。
先从我们平时的使用方法引出这个机制,再结合源码进行分析。
我们平时使用是这样的:
//1. 主线程 handler handler = new myhandler(); //2. 非主线程 handlerthread handlerthread = new handlerthread("handlerthread"); handlerthread.start(); handler handler = new handler(handlerthread.getlooper()); //发送消息 handler.sendmessage(msg); //接收消息 static class myhandler extends handler { //对于非主线程处理消息需要传looper,主线程有默认的smainlooper public myhandler(looper looper) { super(looper); } @override public void handlemessage(message msg) { super.handlemessage(msg); } }
那么为什么初始化的时候,我们执行了1或2,后面只需要sendmessage就可处理任务了呢?学姐这里以非主线程为例进行介绍,handlerthread.start()的时候,实际上创建了一个用于消息循环的looper和消息队列messagequeue,同时启动了消息循环,并将这个循环传给handler,这个循环会从messagequeue中依次取任务出来执行。用户若要执行某项任务,只需要调用handler.sendmessage即可,这里做的事情是将消息添加到messaequeue中。对于主线程也类似,只是主线程smainthread和smainlooper不需要我们主动去创建,程序启动的时候application就创建好了,我们只需要创建handler即可。
我们这里提到了几个概念:
- handlerthread 支持消息循环的线程
- handler 消息处理器
- looper 消息循环对象
- messagequeue 消息队列
- message 消息体
对应关系是:一对多,即(一个)handlerthread、looper、messagequeue -> (多个)handler、message
源码解析
1. looper
(1)创建消息循环
prepare()用于创建looper消息循环对象。looper对象通过一个成员变量threadlocal进行保存。
(2)获取消息循环对象
mylooper()用于获取当前消息循环对象。looper对象从成员变量threadlocal中获取。
(3)开始消息循环
loop()开始消息循环。循环过程如下:
每次从消息队列messagequeue中取出一个message
使用message对应的handler处理message
已处理的message加到本地消息池,循环复用
循环以上步骤,若没有消息表明消息队列停止,退出循环
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)); } public static looper mylooper() { return sthreadlocal.get(); } 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; } // this must be in a local variable, in case a ui event sets the logger printer logging = me.mlogging; if (logging != null) { logging.println(">>>>> dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchmessage(msg); if (logging != null) { logging.println("<<<<< finished to " + msg.target + " " + msg.callback); } // make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newident = binder.clearcallingidentity(); if (ident != newident) { log.wtf(tag, "thread identity changed from 0x" + long.tohexstring(ident) + " to 0x" + long.tohexstring(newident) + " while dispatching to " + msg.target.getclass().getname() + " " + msg.callback + " what=" + msg.what); } msg.recycleunchecked(); } }
2. handler
(1)发送消息
handler支持2种消息类型,即runnable和message。因此发送消息提供了post(runnable r)和sendmessage(message msg)两个方法。从下面源码可以看出runnable赋值给了message的callback,最终也是封装成message对象对象。学姐个人认为外部调用不统一使用message,应该是兼容java的线程任务,学姐认为这种思想也可以借鉴到平常开发过程中。发送的消息都会入队到messagequeue队列中。
(2)处理消息
looper循环过程的时候,是通过dispatchmessage(message msg)对消息进行处理。处理过程:先看是否是runnable对象,如果是则调用handlecallback(msg)进行处理,最终调到runnable.run()方法执行线程;如果不是runnable对象,再看外部是否传入了callback处理机制,若有则使用外部callback进行处理;若既不是runnable对象也没有外部callback,则调用handlemessage(msg),这个也是我们开发过程中最常覆写的方法了。
(3)移除消息
removecallbacksandmessages(),移除消息其实也是从messagequeue中将message对象移除掉。
public void handlemessage(message msg) { } public void dispatchmessage(message msg) { if (msg.callback != null) { handlecallback(msg); } else { if (mcallback != null) { if (mcallback.handlemessage(msg)) { return; } } handlemessage(msg); } } private static void handlecallback(message message) { message.callback.run(); } public final message obtainmessage() { return message.obtain(this); } public final boolean post(runnable r) { return sendmessagedelayed(getpostmessage(r), 0); } public final boolean sendmessage(message msg) { return sendmessagedelayed(msg, 0); } private static message getpostmessage(runnable r) { message m = message.obtain(); m.callback = r; return m; } public final boolean sendmessagedelayed(message msg, long delaymillis) { if (delaymillis < 0) { delaymillis = 0; } return sendmessageattime(msg, systemclock.uptimemillis() + delaymillis); } public boolean sendmessageattime(message msg, long uptimemillis) { messagequeue queue = mqueue; if (queue == null) { runtimeexception e = new runtimeexception( this + " sendmessageattime() called with no mqueue"); log.w("looper", e.getmessage(), e); return false; } return enqueuemessage(queue, msg, uptimemillis); } private boolean enqueuemessage(messagequeue queue, message msg, long uptimemillis) { msg.target = this; if (masynchronous) { msg.setasynchronous(true); } return queue.enqueuemessage(msg, uptimemillis); } public final void removecallbacksandmessages(object token) { mqueue.removecallbacksandmessages(this, token); }
3. messagequeue
(1)消息入队
消息入队方法enqueuemessage(message msg, long when)。其处理过程如下:
待入队的message标记为inuse,when赋值
若消息链表mmessages为空为空,或待入队message执行时间小于mmessage链表头,则待入队message添加到链表头
若不符合以上条件,则轮询链表,根据when从低到高的顺序,插入链表合适位置
(2)消息轮询
next()依次从messagequeue中取出message
(3)移除消息
removemessages()可以移除消息,做的事情实际上就是将消息从链表移除,同时将移除的消息添加到消息池,提供循环复用。
boolean enqueuemessage(message msg, long when) { if (msg.target == null) { throw new illegalargumentexception("message must have a target."); } if (msg.isinuse()) { throw new illegalstateexception(msg + " this message is already in use."); } synchronized (this) { if (mquitting) { illegalstateexception e = new illegalstateexception( msg.target + " sending message to a handler on a dead thread"); log.w("messagequeue", e.getmessage(), e); msg.recycle(); return false; } 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); } } return true; } 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 (false) log.v("messagequeue", "returning message: " + msg); 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); } // 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("messagequeue", "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; } } void removemessages(handler h, int what, object object) { if (h == null) { return; } synchronized (this) { message p = mmessages; // remove all messages at front. while (p != null && p.target == h && p.what == what && (object == null || p.obj == object)) { message n = p.next; mmessages = n; p.recycleunchecked(); p = n; } // remove all messages after front. while (p != null) { message n = p.next; if (n != null) { if (n.target == h && n.what == what && (object == null || n.obj == object)) { message nn = n.next; n.recycleunchecked(); p.next = nn; continue; } } p = n; } } }
4. message
(1)消息创建
message.obtain()创建消息。若消息池链表spool不为空,则从spool中获取第一个,flags标记为uninuse,同时从spool中移除,spoolsize减1;若消息池链表spool为空,则new message()
(2)消息释放
recycle()将消息释放,从内部实现recycleunchecked()可知,将flags标记为inuse,其他各种状态清零,同时将message添加到spool,且spoolsize加1
/** * return a new message instance from the global pool. allows us to * avoid allocating new objects in many cases. */ public static message obtain() { synchronized (spoolsync) { if (spool != null) { message m = spool; spool = m.next; m.next = null; m.flags = 0; // clear in-use flag spoolsize--; return m; } } return new message(); } /** * return a message instance to the global pool. * <p> * you must not touch the message after calling this function because it has * effectively been freed. it is an error to recycle a message that is currently * enqueued or that is in the process of being delivered to a handler. * </p> */ public void recycle() { if (isinuse()) { if (gcheckrecycle) { throw new illegalstateexception("this message cannot be recycled because it " + "is still in use."); } return; } recycleunchecked(); } /** * recycles a message that may be in-use. * used internally by the messagequeue and looper when disposing of queued messages. */ void recycleunchecked() { // mark the message as in use while it remains in the recycled object pool. // clear out all other details. flags = flag_in_use; what = 0; arg1 = 0; arg2 = 0; obj = null; replyto = null; sendinguid = -1; when = 0; target = null; callback = null; data = null; synchronized (spoolsync) { if (spoolsize < max_pool_size) { next = spool; spool = this; spoolsize++; } } }
5. handlerthread
由于java中的thread是没有消息循环机制的,run()方法执行完,线程则结束。handlerthread通过使用looper实现了消息循环,只要不主动调用handlerthread或looper的quit()方法,循环就是一直走下去。
public class handlerthread extends thread { int mpriority; int mtid = -1; looper mlooper; public handlerthread(string name) { super(name); mpriority = process.thread_priority_default; } @override public void run() { mtid = process.mytid(); looper.prepare(); synchronized (this) { mlooper = looper.mylooper(); notifyall(); } process.setthreadpriority(mpriority); onlooperprepared(); looper.loop(); mtid = -1; } public looper getlooper() { if (!isalive()) { return null; } // if the thread has been started, wait until the looper has been created. synchronized (this) { while (isalive() && mlooper == null) { try { wait(); } catch (interruptedexception e) { } } } return mlooper; } public boolean quit() { looper looper = getlooper(); if (looper != null) { looper.quit(); return true; } return false; } }
总结
- 关键类:handlerthread、handler、looper、messagequeue、messaga
- messagequeue数据结构,链表。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!
下一篇: 如果不爱了,请放手