有关Handler机制原理的总结
2017-03-31 15:30
155 查看
Handler是线程与线程间进行通信的一套机制。
Handler是常被开发者拿来更新UI的一种消息处理机制,它的运行机制需要底层的Looper和MessageQueue的支撑。
一个Android应用程序被创建时就会创建一个进程,该进程用应用的包名作为进程名。该进程会启动主线程ActivityThread,也叫做UI主线程,但有时需要做些耗时操作,为了不能够去阻塞UI主线程的正常运行,我们将它放在子线程中进行操作,操作完成后需要绘制UI,但Android子线程不能直接操作UI线程的,所以通过Handler来进行通信。
为什么Android子线程不能直接操作主线程?Android UI线程不是线程安全的,如果多线程并发的话就会造成界面混乱,不可控的状态。那为什么不能让主程序加上锁机制,这样就能够线程安全了?可上锁就会有造成访问的逻辑变得很麻烦、很复杂,并且会阻塞其他线程的执行。综上问题,Android采用单线程模型来处理UI操作。
Handler机制原理
Handler机制是由Looper和MessageQueue来构建消息机制的。
MessageQueue:消息队列。虽然名为队列,但事实上它的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表的,其中主要有插入enqueue()和从中拿走并删除next()两个方法。
Looper:消息循环。MessageQueue来存储消息,Looper则是以无限循环的方式去查找是否有新消息,如有就去处理,若没有就standby(等待)。一个线程创建Handler时首先需要创建Looper的,不然报错:RuntimeException: No Looper; Looper.prepare() wasn't called on this thread,而且每个线程下只需要创建一个Looper,不然会报错:RuntimeException: Only one Looper
may be created per thread。
但是UI线程是不需要创建的,是因为ActivityTread创建时就初始化了Looper,所以在UI主线程就能直接使用Handler。
在特定的线程中Handler使用当前的Looper来构建消息循环系统,那handler是如何获取到当前线程的Looper的?是通过ThreadLocal来获取每个线程的Looper的,ThreadLocal是一个线程内部的数据存储类。它是一个泛型类,里面有set()和get()两个主要方法。有关ThreadLocal的了解可参考Blog:ThreadLocal
Handler通过send Message或者post去将消息发送到messageQueue中,会调用enqueueMessage()方法,而Message Queue的next()方法会将该消息返回给Looper,Looper接收到消息,就会处理--Looper就交由handler的dispatchMessage方法
最后,就是我们经常见到的重写handlerMessage方法去处理消息及UI操作了。
Handler是常被开发者拿来更新UI的一种消息处理机制,它的运行机制需要底层的Looper和MessageQueue的支撑。
一个Android应用程序被创建时就会创建一个进程,该进程用应用的包名作为进程名。该进程会启动主线程ActivityThread,也叫做UI主线程,但有时需要做些耗时操作,为了不能够去阻塞UI主线程的正常运行,我们将它放在子线程中进行操作,操作完成后需要绘制UI,但Android子线程不能直接操作UI线程的,所以通过Handler来进行通信。
为什么Android子线程不能直接操作主线程?Android UI线程不是线程安全的,如果多线程并发的话就会造成界面混乱,不可控的状态。那为什么不能让主程序加上锁机制,这样就能够线程安全了?可上锁就会有造成访问的逻辑变得很麻烦、很复杂,并且会阻塞其他线程的执行。综上问题,Android采用单线程模型来处理UI操作。
Handler机制原理
Handler机制是由Looper和MessageQueue来构建消息机制的。
MessageQueue:消息队列。虽然名为队列,但事实上它的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表的,其中主要有插入enqueue()和从中拿走并删除next()两个方法。
Looper:消息循环。MessageQueue来存储消息,Looper则是以无限循环的方式去查找是否有新消息,如有就去处理,若没有就standby(等待)。一个线程创建Handler时首先需要创建Looper的,不然报错:RuntimeException: No Looper; Looper.prepare() wasn't called on this thread,而且每个线程下只需要创建一个Looper,不然会报错:RuntimeException: Only one Looper
may be created per thread。
class TestThread extends Thread{ @Override public void run() { super.run(); Looper.prepare(); Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); //.... } }; Looper.loop(); } }
但是UI线程是不需要创建的,是因为ActivityTread创建时就初始化了Looper,所以在UI主线程就能直接使用Handler。
在特定的线程中Handler使用当前的Looper来构建消息循环系统,那handler是如何获取到当前线程的Looper的?是通过ThreadLocal来获取每个线程的Looper的,ThreadLocal是一个线程内部的数据存储类。它是一个泛型类,里面有set()和get()两个主要方法。有关ThreadLocal的了解可参考Blog:ThreadLocal
Handler通过send Message或者post去将消息发送到messageQueue中,会调用enqueueMessage()方法,而Message Queue的next()方法会将该消息返回给Looper,Looper接收到消息,就会处理--Looper就交由handler的dispatchMessage方法
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.recycleUnchecked(); } }接着,handler的dispatchMessage方法最终会调用handlerMessage方法来处理消息:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
最后,就是我们经常见到的重写handlerMessage方法去处理消息及UI操作了。
相关文章推荐
- 有关ucenter同步的原理(个人总结的一点浅薄见解)
- 几点有关Web Services的技术学习总结
- 对论坛中有关数据类型转换的总结
- DNS查询原理总结
- TCP/IP攻击原理分析总结
- 有关TinyXML使用的简单总结
- 几点有关Web Services的技术学习总结
- 有关search及ftsearch的一点总结
- vb.net学习总结三(DropDownList 中 添加 StringIntObject 对象的原理)
- 网上大多数原理总结,UDP打洞原理.
- 0831【总结】有关在eclipse、lomboz和jboss下的J2EE-EJB
- 有关C语言指针学习思想的随笔常用形式的总结(原创)
- TCP/IP攻击原理分析总结
- 有关 sizeof和strlen的总结[原创,总结]
- 有关c的一些总结
- Taglib原理和实现 第六章:标签内常用方法总结
- 控件开发中与图元移动有关的几个个小技巧(DTE知识总结)
- 有关论文的个人总结
- 有关UNICODE、ANSI字符集和相关字符串操作的总结
- 有关因特网接入的一点总结