Handler详解
- 在ui线程中我们之后new Handelr()然后实现子线程和主线程的通讯是没有问题的。那是因为在ActivityThread类中调用了Loop.Preapre();和Looper.loop()方法 现在我们在子线程模拟这个过程
- 在子线程中
代码如下
new Thread(){
public void run() {Handler handler=new Handler(); }; }.start();
- 发现异常
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
- 我们只是写了new Handler()就报了一个异常,因此我们查看下构造方法
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
} - 构造方法中先调用Looper.myLooper() ,之后判断是否为null,如果为null 抛出异常 因此我们可以肯定,这里的mLooper肯定为null
- 因此我们得查看Looper.myLooper()方法是怎么工作的
- Looper.myLooper(); 源码
public static Looper myLooper() {
return sThreadLocal.get();
} - 这里引入了一个sThreadLocal.get();本地线程的get方法,我们继续查看get()返回的是什么?
- 首先我们产看ThreadLocal类的功能
Implements a thread-local storage, that is, a variable for which each thread
has its own value. All threads share the same {@code ThreadLocal} object- 发现异常
发现ThreadLocal就是给线程储存了一个共享的对象
接下来我们看一看Looper.Prepare() 方法完成了什么
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将我们的Looper对象存放到了线程中
- 这也就能解释当我们new Hander()的时候,抛出异常,是因为直接Looper.myLooper();拿到的为空,如果我们prepare()的话 ,就给sThreadLocal存放了一个looper,因此也就不为null了,通过源代码,也可以发现一个线程只能有一个looper和Message,但是可以有多个handelr
- 那么looper.loop()方法的作用是什么呢?是让消息泵循环起来,去Messagequeue中读取消息,不loop起来的话 sendMessage()无效
` 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(); }
}`
- 大家看源代码可以看到 Looper.loop()首先拿到自己的Looper,之后拿到消息队列,也就是MessageQueue,因此可以看到MessageQueue在Looper中,因此一个线程只能有一个Looper,也只能有一个MessageQueue
- 之后是一个死循环,不断的调用msg.next();可能被阻塞,类似Socket的服务器端,之后分发出去
接着我们看sendMessage方法
` private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
`
- 最终将消息存放到了消息队列中 ,looper的话就直接从MessageQueue死循环读取
总结
- handler.sendMessage()的功能是将Message()放到Hander中的MessageQueue中
- Looper.prepare()是将Looper绑定到本线程ThreadLocal中
- new Handelr()的时候 获得了ThreadLocal中绑定的Looper
- Looper.loop()的时候,是死循环遍历MessageQueue,拿到消息给了msg.target,这个target指的就是发送消息的那个handelr
- 一个thread中只能有一个Looper和MessageQueue,但是可以多个handler,因为在handler.sendMessage()的时候,Message被放到messagequeue的时候,已经和发送的hanlder绑定了,之后再loop的时候是msg.target.dispatch分发出去
- handler和Looper共用的是MessageQueue
- Messagequeue是Looper的一个成员变量
- ThreadLocal 是保存线程共享的内容的,这里用来保存了Looper