Android消息处理机制Looper和Handler详解
message:消息,其中包含了消息id,消息处理对象以及处理的数据等,由messagequeue统一列队,终由handler处理。 handler:处理者,负责message的发送及处理。使用handler时,需要实现handlemessage(message msg)方法来对特定的message进行处理,例如更新ui等。 messagequeue:消息队列,用来存放handler发送过来的消息,并按照fifo规则执行。当然,存放message并非实际意义的保存,而是将message以链表的方式串联起来的,等待looper的抽取。 looper:消息泵,不断地从messagequeue中抽取message执行。因此,一个messagequeue需要一个looper。 thread:线程,负责调度整个消息循环,即消息循环的执行场所。
android系统的消息队列和消息循环都是针对具体线程的,一个线程可以存在(当然也可以不存在)一个消息队列和一个消 息循环(looper),特定线程的消息只能分发给本线程,不能进行跨线程,跨进程通讯。但是创建的工作线程默认是没有消息循环和消息队列的,如果想让该 线程具有消息队列和消息循环,需要在线程中首先调用looper.prepare()来创建消息队列,然后调用looper.loop()进入消息循环。 如下例所示:
looperthread thread { handler mhandler; run() { looper.prepare(); mhandler = handler() { handlemessage(message msg) { } }; looper.loop(); } }
//looper类分析
//没找到合适的分析代码的办法,只能这么来了。每个重要行的上面都会加上注释
//功能方面的代码会在代码前加上一段分析
public class looper { //static变量,判断是否打印调试信息。 private static final boolean debug = false; private static final boolean locallogv = debug ? config.logd : config.logv; // sthreadlocal.get() will return null unless you've called prepare(). //线程本地存储功能的封装,tls,thread local storage,什么意思呢?因为存储要么在栈上,例如函数内定义的内部变量。要么在堆上,例如new或者malloc出来的东西 //但是现在的系统比如linux和windows都提供了线程本地存储空间,也就是这个存储空间是和线程相关的,一个线程内有一个内部存储空间,这样的话我把线程相关的东西就存储到 //这个线程的tls中,就不用放在堆上而进行同步操作了。 private static final threadlocal sthreadlocal = new threadlocal(); //消息队列,messagequeue,看名字就知道是个queue.. final messagequeue mqueue; volatile boolean mrun; //和本looper相关的那个线程,初始化为null thread mthread; private printer mlogging = null; //static变量,代表一个ui process(也可能是service吧,这里默认就是ui)的主线程 private static looper mmainlooper = null; /** initialize the current thread as a looper. * this gives you a chance to create handlers that then reference * this looper, before actually starting the loop. be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ //往tls中设上这个looper对象的,如果这个线程已经设过了looper的话就会报错 //这说明,一个线程只能设一个looper public static final void prepare() { if (sthreadlocal.get() != null) { throw new runtimeexception("only one looper may be created per thread"); } sthreadlocal.set(new looper()); } /** initialize the current thread as a looper, marking it as an application's main * looper. the main looper for your application is created by the android environment, * so you should never need to call this function yourself. * {@link #prepare()} */ //由framework设置的ui程序的主消息循环,注意,这个主消息循环是不会主动退出的 // public static final void preparemainlooper() { prepare(); setmainlooper(mylooper()); //判断主消息循环是否能退出.... //通过quit函数向looper发出退出申请 if (process.supportsprocesses()) { mylooper().mqueue.mquitallowed = false; } } private synchronized static void setmainlooper(looper looper) { mmainlooper = looper; } /** returns the application's main looper, which lives in the main thread of the application. */ public synchronized static final looper getmainlooper() { return mmainlooper; } /** * run the message queue in this thread. be sure to call * {@link #quit()} to end the loop. */ //消息循环,整个程序就在这里while了。 //这个是static函数喔! public static final void loop() { looper me = mylooper();//从该线程中取出对应的looper对象 messagequeue queue = me.mqueue;//取消息队列对象... while (true) { message msg = queue.next(); // might block取消息队列中的一个待处理消息.. //if (!me.mrun) {//是否需要退出?mrun是个volatile变量,跨线程同步的,应该是有地方设置它。 // break; //} if (msg != null) { if (msg.target == null) { // no target is a magic identifier for the quit message. return; } if (me.mlogging!= null) me.mlogging.println( ">>>>> dispatching to " + msg.target + " " + msg.callback + ": " + msg.what ); msg.target.dispatchmessage(msg); if (me.mlogging!= null) me.mlogging.println( "<<<<< finished to " + msg.target + " " + msg.callback); msg.recycle(); } } } /** * return the looper object associated with the current thread. returns * null if the calling thread is not associated with a looper. */ //返回和线程相关的looper public static final looper mylooper() { return (looper)sthreadlocal.get(); } /** * control logging of messages as they are processed by this looper. if * enabled, a log message will be written to <var>printer</var> * at the beginning and ending of each message dispatch, identifying the * target handler and message contents. * * @param printer a printer object that will receive log messages, or * null to disable message logging. */ //设置调试输出对象,looper循环的时候会打印相关信息,用来调试用最好了。 public void setmessagelogging(printer printer) { mlogging = printer; } /** * return the {@link messagequeue} object associated with the current * thread. this must be called from a thread running a looper, or a * nullpointerexception will be thrown. */ public static final messagequeue myqueue() { return mylooper().mqueue; } //创建一个新的looper对象, //内部分配一个消息队列,设置mrun为true private looper() { mqueue = new messagequeue(); mrun = true; mthread = thread.currentthread(); } public void quit() { message msg = message.obtain(); // note: by enqueueing directly into the message queue, the // message is left with a null target. this is how we know it is // a quit message. mqueue.enqueuemessage(msg, 0); } /** * return the thread associated with this looper. */ public thread getthread() { return mthread; } //后面就简单了,打印,异常定义等。 public void dump(printer pw, string prefix) { pw.println(prefix + this); pw.println(prefix + "mrun=" + mrun); pw.println(prefix + "mthread=" + mthread); pw.println(prefix + "mqueue=" + ((mqueue != null) ? mqueue : "(null")); if (mqueue != null) { synchronized (mqueue) { message msg = mqueue.mmessages; int n = 0; while (msg != null) { pw.println(prefix + " message " + n + ": " + msg); n++; msg = msg.next; } pw.println(prefix + "(total messages: " + n + ")"); } } } public string tostring() { return "looper{" + integer.tohexstring(system.identityhashcode(this)) + "}"; } static class handlerexception extends exception { handlerexception(message message, throwable cause) { super(createmessage(cause), cause); } static string createmessage(throwable cause) { string causemsg = cause.getmessage(); if (causemsg == null) { causemsg = cause.tostring(); } return causemsg; } } }
那怎么往这个消息队列中发送消息呢??调用looper的static函数myqueue可以获得消息队列,这样你就可用自己往里边插入消息了。不过这种方法比较麻烦,这个时候handler类就发挥作用了。先来看看handler的代码,就明白了。
class handler{ .......... //handler默认构造函数 public handler() { //这个if是干嘛用的暂时还不明白,涉及到java的深层次的内容了应该 if (find_potential_leaks) { final class<? extends handler> klass = getclass(); if ((klass.isanonymousclass() || klass.ismemberclass() || klass.islocalclass()) && (klass.getmodifiers() & modifier.static) == 0) { log.w(tag, "the following handler class should be static or leaks might occur: " + klass.getcanonicalname()); } } //获取本线程的looper对象 //如果本线程还没有设置looper,这回抛异常 mlooper = looper.mylooper(); if (mlooper == null) { throw new runtimeexception( "can't create handler inside thread that has not called looper.prepare()"); } //无耻啊,直接把looper的queue和自己的queue搞成一个了 //这样的话,我通过handler的封装机制加消息的话,就相当于直接加到了looper的消息队列中去了 mqueue = mlooper.mqueue; mcallback = null; } //还有好几种构造函数,一个是带callback的,一个是带looper的 //由外部设置looper public handler(looper looper) { mlooper = looper; mqueue = looper.mqueue; mcallback = null; } // 带callback的,一个handler可以设置一个callback。如果有callback的话, //凡是发到通过这个handler发送的消息,都有callback处理,相当于一个总的集中处理 //待会看dispatchmessage的时候再分析 public handler(looper looper, callback callback) { mlooper = looper; mqueue = looper.mqueue; mcallback = callback; } // //通过handler发送消息 //调用了内部的一个sendmessagedelayed public final boolean sendmessage(message msg) { return sendmessagedelayed(msg, 0); } //ft,又封装了一层,这回是调用sendmessageattime了 //因为延时时间是基于当前调用时间的,所以需要获得绝对时间传递给sendmessageattime 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) { boolean sent = false; messagequeue queue = mqueue; if (queue != null) { //把消息的target设置为自己,然后加入到消息队列中 //对于队列这种数据结构来说,操作比较简单了 msg.target = this; sent = queue.enqueuemessage(msg, uptimemillis); } else { runtimeexception e = new runtimeexception( this + " sendmessageattime() called with no mqueue"); log.w("looper", e.getmessage(), e); } return sent; } //还记得looper中的那个消息循环处理吗 //从消息队列中得到一个消息后,会调用它的target的dispatchmesage函数 //message的target已经设置为handler了,所以 //最后会转到handler的msg处理上来 //这里有个处理流程的问题 public void dispatchmessage(message msg) { //如果msg本身设置了callback,则直接交给这个callback处理了 if (msg.callback != null) { handlecallback(msg); } else { //如果该handler的callback有的话,则交给这个callback处理了---相当于集中处理 if (mcallback != null) { if (mcallback.handlemessage(msg)) { return; } } //否则交给派生处理,基类默认处理是什么都不干 handlemessage(msg); } } .......... }
生成
message msg = mhandler.obtainmessage(); msg.what = what; msg.sendtotarget();
发送
messagequeue queue = mqueue; if (queue != null) { msg.target = this; sent = queue.enqueuemessage(msg, uptimemillis); }
在handler.java的sendmessageattime(message msg, long uptimemillis)方法中,我们看到,它找到它所引用的messagequeue,然后将message的target设定成自己(目的是为了在处理消息环节,message能找到正确的handler),再将这个message纳入到消息队列中。
抽取
looper me = mylooper(); messagequeue queue = me.mqueue; while (true) { message msg = queue.next(); // might block if (msg != null) { if (msg.target == null) { // no target is a magic identifier for the quit message. return; } msg.target.dispatchmessage(msg); msg.recycle(); } }
在looper.java的loop()函数里,我们看到,这里有一个死循环,不断地从messagequeue中获取下一个(next方法)message,然后通过message中携带的target信息,交由正确的handler处理(dispatchmessage方法)。
处理
if (msg.callback != null) { handlecallback(msg); } else { if (mcallback != null) { if (mcallback.handlemessage(msg)) { return; } } handlemessage(msg); }
在handler.java的dispatchmessage(message msg)方法里,其中的一个分支就是调用handlemessage方法来处理这条message,而这也正是我们在职责处描述使用handler时需要实现handlemessage(message msg)的原因。
至于dispatchmessage方法中的另外一个分支,我将会在后面的内容中说明。
至此,我们看到,一个message经由handler的发送,messagequeue的入队,looper的抽取,又再一次地回到handler的怀抱。而绕的这一圈,也正好帮助我们将同步操作变成了异步操作。
3)剩下的部分,我们将讨论一下handler所处的线程及更新ui的方式。
在主线程(ui线程)里,如果创建handler时不传入looper对象,那么将直接使用主线程(ui线程)的looper对象(系统已经帮我们创建了);在其它线程里,如果创建handler时不传入looper对象,那么,这个handler将不能接收处理消息。在这种情况下,通用的作法是:
class looperthread extends thread { public handler mhandler; public void run() { looper.prepare(); mhandler = new handler() { public void handlemessage(message msg) { // process incoming messages here } }; looper.loop(); } }
在创建handler之前,为该线程准备好一个looper(looper.prepare),然后让这个looper跑起来(looper.loop),抽取message,这样,handler才能正常工作。
因此,handler处理消息总是在创建handler的线程里运行。而我们的消息处理中,不乏更新ui的操作,不正确的线程直接更新ui将引发异常。因此,需要时刻关心handler在哪个线程里创建的。
如何更新ui才能不出异常呢?sdk告诉我们,有以下4种方式可以从其它线程访问ui线程:
· activity.runonuithread(runnable)
· view.post(runnable)
· view.postdelayed(runnable, long)
· handler
其中,重点说一下的是view.post(runnable)方法。在post(runnable action)方法里,view获得当前线程(即ui线程)的handler,然后将action对象post到handler里。在handler里,它将传递过来的action对象包装成一个message(message的callback为action),然后将其投入ui线程的消息循环中。在handler再次处理该message时,有一条分支(未解释的那条)就是为它所设,直接调用runnable的run方法。而此时,已经路由到ui线程里,因此,我们可以毫无顾虑的来更新ui。
4) 几点小结
· handler的处理过程运行在创建handler的线程里
· 一个looper对应一个messagequeue
· 一个线程对应一个looper
· 一个looper可以对应多个handler
· 不确定当前线程时,更新ui时尽量调用post方法
推荐阅读
-
Android消息处理机制Looper和Handler详解
-
Android Studio 之 Android消息机制 之简单Demo --- 使用Handler和Message类来完成消息传递
-
Android消息机制(3)- Handler和Looper
-
Android消息通信机制Handler详解,Handler,Looper,MessageQueue,源码解析,讲解这几个类怎么配合工作的
-
Android消息处理机制(Handler、Looper、MessageQueue与Message)
-
Android异步消息处理机制 深入理解Looper、Handler、Message的关系
-
简述android线程间消息处理机制(Looper、Handler和Message)
-
Android消息机制三剑客之Handler、Looper、Message源码分析(一)
-
Android消息机制原理,仿写Handler Looper源码解析跨线程通信原理--之仿写模拟Handler(四)
-
Android异步消息机制-深入理解Handler、Looper和MessageQueue之间的关系