您的位置:首页 > 其它

handler机制详解

2016-03-05 13:47 302 查看

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: