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
相关文章推荐
- hdu 1232 畅通工程(基础模板)
- Bellman-Ford(贝尔曼,福特)算法——解决负权边
- 30道四则运算
- DataGrid 刷新选中问题
- oc去除空格方法和判断空格方法
- 白话Spring(基础篇)---AOP(实例篇-4)
- Android几种在其他线程中更新UI的方法
- PAT1039
- Android 将assets中的文件拷贝到本地SD卡使用
- 安装zookeeper
- 软件工程实验一--编程随机生成30个四则运算,算数包括100以内的整数和真分数。
- UUID 和 GUID 的区别
- Uva536——Tree Recovery
- 连续子数组的最大和 java实现
- <十>RecycleView点击事件
- Android开发学习之路--【前言】
- 【学习C++】1.开始学习C++
- SSL握手
- UESTC P酱的冒险旅途 785 (规律模拟)
- 1026. Table Tennis (30)