全面总结Android中线程的异步处理方式
一、概述
handler 、 looper 、message 这三者都与android异步消息处理线程相关的概念。那么什么叫异步消息处理线程呢?
异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。
说了这一堆,那么和handler 、 looper 、message有啥关系?其实looper负责的就是创建一个messagequeue,然后进入一个无限循环体不断从该messagequeue中读取消息,而消息的创建者就是一个或多个handler 。
二、源码解析
1、looper
对于looper主要是prepare()和loop()两个方法。
首先看prepare()方法
public static final void prepare() { if (sthreadlocal.get() != null) { throw new runtimeexception("only one looper may be created per thread"); } sthreadlocal.set(new looper(true)); }
sthreadlocal是一个threadlocal对象,可以在一个线程中存储变量。可以看到,在第5行,将一个looper的实例放入了threadlocal,并且2-4行判断了sthreadlocal是否为null,否则抛出异常。这也就说明了looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个looper实例~相信有些哥们一定遇到这个错误。
下面看looper的构造方法:
private looper(boolean quitallowed) { mqueue = new messagequeue(quitallowed); mrun = true; mthread = thread.currentthread(); }
在构造方法中,创建了一个messagequeue(消息队列)。
然后我们看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; } // 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.recycle(); } }
第2行:
public static looper mylooper() { return sthreadlocal.get(); }
方法直接返回了sthreadlocal存储的looper实例,如果me为null则抛出异常,也就是说looper方法必须在prepare方法之后运行。
第6行:拿到该looper实例中的mqueue(消息队列)
13到45行:就进入了我们所说的无限循环。
14行:取出一条消息,如果没有消息则阻塞。
27行:使用调用 msg.target.dispatchmessage(msg);把消息交给msg的target的dispatchmessage方法去处理。msg的target是什么呢?其实就是handler对象,下面会进行分析。
44行:释放消息占据的资源。
looper主要作用:
(1)与当前线程绑定,保证一个线程只会有一个looper实例,同时一个looper实例也只有一个messagequeue。
(2)loop()方法,不断从messagequeue中去取消息,交给消息的target属性的dispatchmessage去处理。
好了,我们的异步消息处理线程已经有了消息队列(messagequeue),也有了在无限循环体中取出消息的哥们,现在缺的就是发送消息的对象了,于是乎:handler登场了。
2、handler
使用handler之前,我们都是初始化一个实例,比如用于更新ui线程,我们会在声明的时候直接初始化,或者在oncreate中初始化handler实例。所以我们首先看handler的构造方法,看其如何与messagequeue联系上的,它在子线程中发送的消息(一般发送消息都在非ui线程)怎么发送到messagequeue中的。
public handler() { this(null, false); } public handler(callback callback, boolean async) { 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()); } } mlooper = looper.mylooper(); if (mlooper == null) { throw new runtimeexception( "can't create handler inside thread that has not called looper.prepare()"); } mqueue = mlooper.mqueue; mcallback = callback; masynchronous = async; }
14行:通过looper.mylooper()获取了当前线程保存的looper实例,然后在19行又获取了这个looper实例中保存的messagequeue(消息队列),这样就保证了handler的实例与我们looper实例中messagequeue关联上了。
handler 常用方法:
(1)post(runnable)
(2)postattime(runnable,long)
(3)postdelayed(runnable long)
(4)sendemptymessage(int)
(5)sendmessage(message)
(6)sendmessageattime(message,long)
(7)sendmessagedelayed(message,long)
以上post类方法允许你排列一个runnable对象到主线程队列中, sendmessage类方法, 允许你安排一个带数据的message对象到队列中,等待更新.
一般运行逻辑:
点击button --- > 启动一条新线程,用来处理数据 ---- >数据处理完毕,通过handler返回 ----- > handler里面接收返回的数据,进行ui更新等处理。
然后看我们最常用的sendmessage方法
public final boolean sendmessage(message msg) { return sendmessagedelayed(msg, 0); } public final boolean sendemptymessagedelayed(int what, long delaymillis) { message msg = message.obtain(); msg.what = what; return sendmessagedelayed(msg, delaymillis); } 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); }
辗转反则最后调用了sendmessageattime,在此方法内部有直接获取messagequeue然后调用了enqueuemessage方法,我们再来看看此方法:
private boolean enqueuemessage(messagequeue queue, message msg, long uptimemillis) { msg.target = this; if (masynchronous) { msg.setasynchronous(true); } return queue.enqueuemessage(msg, uptimemillis); }
enqueuemessage中首先为meg.target赋值为this,【如果大家还记得looper的loop方法会取出每个msg然后交给msg,target.dispatchmessage(msg)去处理消息】,也就是把当前的handler作为msg的target属性。最终会调用queue的enqueuemessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。
现在已经很清楚了looper会调用prepare()和loop()方法,在当前执行的线程中保存一个looper实例,这个实例会保存一个messagequeue对象,然后当前线程进入一个无限循环中去,不断从messagequeue中读取handler发来的消息。然后再回调创建这个消息的handler中的dispathmessage方法,下面我们赶快去看一看这个方法:
public void dispatchmessage(message msg) { if (msg.callback != null) { handlecallback(msg); } else { if (mcallback != null) { if (mcallback.handlemessage(msg)) { return; } } handlemessage(msg); } }
可以看到,第10行,调用了handlemessage方法,下面我们去看这个方法:
/** * subclasses must implement this to receive messages. */ public void handlemessage(message msg) { }
可以看到这是一个空方法,为什么呢,因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handlemessage方法,然后根据msg.what进行消息处理。
例如:
private handler mhandler = new handler() { public void handlemessage(android.os.message msg) { switch (msg.what) { case value: break; default: break; } }; };
到此,这个流程已经解释完毕,让我们首先总结一下
(1)首先looper.prepare()在本线程中保存一个looper实例,然后该实例中保存一个messagequeue对象;因为looper.prepare()在一个线程中只能调用一次,所以messagequeue在一个线程中只会存在一个。
(2)looper.loop()会让当前线程进入一个无限循环,不端从messagequeue的实例中读取消息,然后回调msg.target.dispatchmessage(msg)方法。
(3)handler的构造方法,会首先得到当前线程中保存的looper实例,进而与looper实例中的messagequeue想关联。
(4)handler的sendmessage方法,会给msg的target赋值为handler自身,然后加入messagequeue中。
(5)在构造handler实例时,我们会重写handlemessage方法,也就是msg.target.dispatchmessage(msg)最终调用的方法。
好了,总结完成,大家可能还会问,那么在activity中,我们并没有显示的调用looper.prepare()和looper.loop()方法,为啥handler可以成功创建呢,这是因为在activity的启动代码中,已经在当前ui线程调用了looper.prepare()和looper.loop()方法。
3、handler post
今天有人问我,你说handler的post方法创建的线程和ui线程有什么关系?
其实这个问题也是出现这篇博客的原因之一;这里需要说明,有时候为了方便,我们会直接写如下代码:
mhandler.post(new runnable() { @override public void run() { log.e("tag", thread.currentthread().getname()); mtxt.settext("yoxi"); } });
然后run方法中可以写更新ui的代码,其实这个runnable并没有创建什么线程,而是发送了一条消息,下面看源码:
public final boolean post(runnable r) { return sendmessagedelayed(getpostmessage(r), 0); } private static message getpostmessage(runnable r) { message m = message.obtain(); m.callback = r; return m; }
可以看到,在getpostmessage中,得到了一个message对象,然后将我们创建的runable对象作为callback属性,赋值给了此message.
注:产生一个message对象,可以new ,也可以使用message.obtain()方法;两者都可以,但是更建议使用obtain方法,因为message内部维护了一个message池用于message的复用,避免使用new 重新分配内存。
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); }
最终和handler.sendmessage一样,调用了sendmessageattime,然后调用了enqueuemessage方法,给msg.target赋值为handler,最终加入messagqueue.
可以看到,这里msg的callback和target都有值,那么会执行哪个呢?
其实上面已经贴过代码,就是dispatchmessage方法:
public void dispatchmessage(message msg) { if (msg.callback != null) { handlecallback(msg); } else { if (mcallback != null) { if (mcallback.handlemessage(msg)) { return; } } handlemessage(msg); } }
第2行,如果不为null,则执行callback回调,也就是我们的runnable对象。
好了,关于looper , handler , message 这三者关系上面已经叙述的非常清楚了。
最后来张图解:
希望图片可以更好的帮助大家的记忆~~
三、补充
其实handler不仅可以更新ui,你完全可以在一个子线程中去创建一个handler,然后使用这个handler实例在任何其他线程中发送消息,最终处理消息的代码都会在你创建handler实例的线程中运行。
eg:
new thread() { private handler handler; public void run() { looper.prepare(); handler = new handler() { public void handlemessage(android.os.message msg) { log.e("tag",thread.currentthread().getname()); }; } }
android不仅给我们提供了异步消息处理机制让我们更好的完成ui的更新,其实也为我们提供了异步消息处理机制代码的参考~~不仅能够知道原理,最好还可以将此设计用到其他的非android项目中去~~