Android消息处理-源码分析篇
来源:互联网 发布:人工智能计算器苹果版 编辑:程序博客网 时间:2024/05/06 09:10
AndroidLooper和Handler分析
第一次接触android应用程序(这里指的是JAVA层的UI程序,也难怪了,Google放出的API就只支持JAVA应用程序了),很难搞明白内部是如何实现的。但是,从原理上分析,应该是有一个消息循环,一个消息队列,然后主线程不断得从消息队列中取得消息并处理之。
然而,google封装得太厉害了,所以一时半会还是搞不清楚到底是怎么做的。本文将分析android内的looper,这个是用来封装消息循环和消息队列的一个类,handler其实可以看做是一个工具类,用来向消息队列中插入消息的。好比是Windows API的SendMessage中的HANDLE,这个handle是窗口句柄。
1. //Looper类分析 2. //没找到合适的分析代码的办法,只能这么来了。每个重要行的上面都会加上注释 3. //功能方面的代码会在代码前加上一段分析 4. public class Looper { 5. //static变量,判断是否打印调试信息。 6. private static final boolean DEBUG = false; 7. private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; 8. 9. // sThreadLocal.get() will return null unless you've called prepare(). 10. //线程本地存储功能的封装,TLS,thread local storage,什么意思呢?因为存储要么在栈上,例如函数内定义的内部变量。要么在堆上,例如new或者malloc出来的东西 11. //但是现在的系统比如Linux和windows都提供了线程本地存储空间,也就是这个存储空间是和线程相关的,一个线程内有一个内部存储空间,这样的话我把线程相关的东西就存储到 12. //这个线程的TLS中,就不用放在堆上而进行同步操作了。 13. private static final ThreadLocal sThreadLocal = new ThreadLocal(); 14. //消息队列,MessageQueue,看名字就知道是个queue.. 15. final MessageQueue mQueue; 16. volatile boolean mRun; 17. //和本looper相关的那个线程,初始化为null 18. Thread mThread; 19. private Printer mLogging = null; 20. //static变量,代表一个UI Process(也可能是service吧,这里默认就是UI)的主线程 21. private static Looper mMainLooper = null; 22. 23. /** Initialize the current thread as a looper. 24. * This gives you a chance to create handlers that then reference 25. * this looper, before actually starting the loop. Be sure to call 26. * {@link #loop()} after calling this method, and end it by calling 27. * {@link #quit()}. 28. */ 29. //往TLS中设上这个Looper对象的,如果这个线程已经设过了looper的话就会报错 30. //这说明,一个线程只能设一个looper 31. public static final void prepare() { 32. if (sThreadLocal.get() != null) { 33. throw new RuntimeException("Only one Looper may be created per thread"); 34. } 35. sThreadLocal.set(new Looper()); 36. } 37. 38. /** Initialize the current thread as a looper, marking it as an application's main 39. * looper. The main looper for your application is created by the Android environment, 40. * so you should never need to call this function yourself. 41. * {@link #prepare()} 42. */ 43. //由framework设置的UI程序的主消息循环,注意,这个主消息循环是不会主动退出的 44. // 45. public static final void prepareMainLooper() { 46. prepare(); 47. setMainLooper(myLooper()); 48. //判断主消息循环是否能退出.... 49. //通过quit函数向looper发出退出申请 50. if (Process.supportsProcesses()) { 51. myLooper().mQueue.mQuitAllowed = false; 52. } 53. } 54. 55. private synchronized static void setMainLooper(Looper looper) { 56. mMainLooper = looper; 57. } 58. 59. /** Returns the application's main looper, which lives in the main thread of the application. 60. */ 61. public synchronized static final Looper getMainLooper() { 62. return mMainLooper; 63. } 64. 65. /** 66. * Run the message queue in this thread. Be sure to call 67. * {@link #quit()} to end the loop. 68. */ 69. //消息循环,整个程序就在这里while了。 70. //这个是static函数喔! 71. public static final void loop() { 72. Looper me = myLooper();//从该线程中取出对应的looper对象 73. MessageQueue queue = me.mQueue;//取消息队列对象... 74. while (true) { 75. Message msg = queue.next(); // might block取消息队列中的一个待处理消息.. 76. //if (!me.mRun) {//是否需要退出?mRun是个volatile变量,跨线程同步的,应该是有地方设置它。 77. // break; 78. //} 79. if (msg != null) { 80. if (msg.target == null) { 81. // No target is a magic identifier for the quit message. 82. return; 83. } 84. if (me.mLogging!= null) me.mLogging.println( 85. ">>>>> Dispatching to " + msg.target + " " 86. + msg.callback + ": " + msg.what 87. ); 88. msg.target.dispatchMessage(msg); 89. if (me.mLogging!= null) me.mLogging.println( 90. "<<<<< Finished to " + msg.target + " " 91. + msg.callback); 92. msg.recycle(); 93. } 94. } 95. } 96. 97. /** 98. * Return the Looper object associated with the current thread. Returns 99. * null if the calling thread is not associated with a Looper. 100. */ 101.//返回和线程相关的looper 102. public static final Looper myLooper() { 103. return (Looper)sThreadLocal.get(); 104. } 105. 106. /** 107. * Control logging of messages as they are processed by this Looper. If 108. * enabled, a log message will be written to <var>printer</var> 109. * at the beginning and ending of each message dispatch, identifying the 110. * target Handler and message contents. 111. * 112. * @param printer A Printer object that will receive log messages, or 113. * null to disable message logging. 114. */ 115.//设置调试输出对象,looper循环的时候会打印相关信息,用来调试用最好了。 116. public void setMessageLogging(Printer printer) { 117. mLogging = printer; 118. } 119. 120. /** 121. * Return the {@link MessageQueue} object associated with the current 122. * thread. This must be called from a thread running a Looper, or a 123. * NullPointerException will be thrown. 124. */ 125. public static final MessageQueue myQueue() { 126. return myLooper().mQueue; 127. } 128.//创建一个新的looper对象, 129.//内部分配一个消息队列,设置mRun为true 130. private Looper() { 131. mQueue = new MessageQueue(); 132. mRun = true; 133. mThread = Thread.currentThread(); 134. } 135. 136. public void quit() { 137. Message msg = Message.obtain(); 138. // NOTE: By enqueueing directly into the message queue, the 139. // message is left with a null target. This is how we know it is 140. // a quit message. 141. mQueue.enqueueMessage(msg, 0); 142. } 143. 144. /** 145. * Return the Thread associated with this Looper. 146. */ 147. public Thread getThread() { 148. return mThread; 149. } 150. //后面就简单了,打印,异常定义等。 151. public void dump(Printer pw, String prefix) { 152. pw.println(prefix + this); 153. pw.println(prefix + "mRun=" + mRun); 154. pw.println(prefix + "mThread=" + mThread); 155. pw.println(prefix + "mQueue=" + ((mQueue != null) ? mQueue : "(null")); 156. if (mQueue != null) { 157. synchronized (mQueue) { 158. Message msg = mQueue.mMessages; 159. int n = 0; 160. while (msg != null) { 161. pw.println(prefix + " Message " + n + ": " + msg); 162. n++; 163. msg = msg.next; 164. } 165. pw.println(prefix + "(Total messages: " + n + ")"); 166. } 167. } 168. } 169. 170. public String toString() { 171. return "Looper{" 172. + Integer.toHexString(System.identityHashCode(this)) 173. + "}"; 174. } 175. 176. static class HandlerException extends Exception { 177. 178. HandlerException(Message message, Throwable cause) { 179. super(createMessage(cause), cause); 180. } 181. 182. static String createMessage(Throwable cause) { 183. String causeMsg = cause.getMessage(); 184. if (causeMsg == null) { 185. causeMsg = cause.toString(); 186. } 187. return causeMsg; 188. } 189. } 190.}
那怎么往这个消息队列中发送消息呢??调用looper的static函数myQueue可以获得消息队列,这样你就可用自己往里边插入消息了。不过这种方法比较麻烦,这个时候handler类就发挥作用了。先来看看handler的代码,就明白了。
1. class Handler{ 2. .......... 3. //handler默认构造函数 4. public Handler() { 5. //这个if是干嘛用的暂时还不明白,涉及到java的深层次的内容了应该 6. if (FIND_POTENTIAL_LEAKS) { 7. final Class<? extends Handler> klass = getClass(); 8. if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 9. (klass.getModifiers() & Modifier.STATIC) == 0) { 10. Log.w(TAG, "The following Handler class should be static or leaks might occur: " + 11. klass.getCanonicalName()); 12. } 13. } 14. //获取本线程的looper对象 15. //如果本线程还没有设置looper,这回抛异常 16. mLooper = Looper.myLooper(); 17. if (mLooper == null) { 18. throw new RuntimeException( 19. "Can't create handler inside thread that has not called Looper.prepare()"); 20. } 21. //无耻啊,直接把looper的queue和自己的queue搞成一个了 22. //这样的话,我通过handler的封装机制加消息的话,就相当于直接加到了looper的消息队列中去了 23. mQueue = mLooper.mQueue; 24. mCallback = null; 25. } 26. //还有好几种构造函数,一个是带callback的,一个是带looper的 27. //由外部设置looper 28. public Handler(Looper looper) { 29. mLooper = looper; 30. mQueue = looper.mQueue; 31. mCallback = null; 32. } 33. // 带callback的,一个handler可以设置一个callback。如果有callback的话, 34. //凡是发到通过这个handler发送的消息,都有callback处理,相当于一个总的集中处理 35. //待会看dispatchMessage的时候再分析 36. public Handler(Looper looper, Callback callback) { 37. mLooper = looper; 38. mQueue = looper.mQueue; 39. mCallback = callback; 40. } 41. // 42. //通过handler发送消息 43. //调用了内部的一个sendMessageDelayed 44. public final boolean sendMessage(Message msg) 45. { 46. return sendMessageDelayed(msg, 0); 47. } 48. //FT,又封装了一层,这回是调用sendMessageAtTime了 49. //因为延时时间是基于当前调用时间的,所以需要获得绝对时间传递给sendMessageAtTime 50. public final boolean sendMessageDelayed(Message msg, long delayMillis) 51. { 52. if (delayMillis < 0) { 53. delayMillis = 0; 54. } 55. return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); 56. } 57. 58. 59. public boolean sendMessageAtTime(Message msg, long uptimeMillis) 60. { 61. boolean sent = false; 62. MessageQueue queue = mQueue; 63. if (queue != null) { 64. //把消息的target设置为自己,然后加入到消息队列中 65. //对于队列这种数据结构来说,操作比较简单了 66. msg.target = this; 67. sent = queue.enqueueMessage(msg, uptimeMillis); 68. } 69. else { 70. RuntimeException e = new RuntimeException( 71. this + " sendMessageAtTime() called with no mQueue"); 72. Log.w("Looper", e.getMessage(), e); 73. } 74. return sent; 75. } 76. //还记得looper中的那个消息循环处理吗 77. //从消息队列中得到一个消息后,会调用它的target的dispatchMesage函数 78. //message的target已经设置为handler了,所以 79. //最后会转到handler的msg处理上来 80. //这里有个处理流程的问题 81. public void dispatchMessage(Message msg) { 82. //如果msg本身设置了callback,则直接交给这个callback处理了 83. if (msg.callback != null) { 84. handleCallback(msg); 85. } else { 86. //如果该handler的callback有的话,则交给这个callback处理了---相当于集中处理 87. if (mCallback != null) { 88. if (mCallback.handleMessage(msg)) { 89. return; 90. } 91. } 92. //否则交给派生处理,基类默认处理是什么都不干 93. handleMessage(msg); 94. } 95. } 96. .......... 97. }
讲了这么多,该怎么创建和使用一个带消息循环的线程呢?
1. //假设在onCreate中创建一个线程 2. //不花时间考虑代码的完整和严谨性了,以讲述原理为主。 3. .... 4. 5. ... onCreate(...){ 6. 7. //难点是如何把android中的looper和java的thread弄到一起去。 8. //而且还要把随时取得这个looper用来创建handler 9. //最简单的办法就是从Thread派生一个 10. class ThreadWithMessageHandle extends Thread{ 11. //重载run函数 12. Looper myLooper = null; 13. run(){ 14. Looper.prepare();//将Looper设置到这个线程中 15. myLooper = Looper.myLooper(); 16. Looper.loop();开启消息循环 17. } 18. 19. ThreadWithMessageHandle threadWithMgs = new ThreadWithMessageHandle(); 20. threadWithMsg.start(); 21. Looper looper = threadWithMsg.myLooper;// 22. //这里有个问题.threadWithMgs中的myLooper可能此时为空 23. //需要同步处理一下 24. //或者像API文档中的那样,把handler定义到ThreadWithMessageHandle到去。 25. //外线程获得这个handler的时候仍然要注意同步的问题,因为handler的创建是在run中的 26. Handler threadHandler = new Handler(looper); 27. threadHandler.sendMessage(...) 28. } 29. 30. 31. } 32. 33. 34. 35. ...
好了,handler和looper的分析就都这了,其实原理挺简单的。
- Android消息处理-源码分析篇
- Android 消息处理源码分析(1)
- Android 消息处理源码分析(2)
- Android消息处理机制(源码分析为主)
- Android的消息处理机制源码分析
- TOMCAT源码分析(消息处理)
- TOMCAT源码分析(消息处理)
- TOMCAT源码分析(消息处理)
- TOMCAT源码分析(消息处理)
- TOMCAT源码分析(消息处理)
- TOMCAT源码分析(消息处理)
- TOMCAT源码分析(消息处理)
- TOMCAT源码分析(消息处理)
- TOMCAT源码分析(消息处理)
- TOMCAT源码分析(消息处理)
- Android应用程序消息处理机制(Looper、Handler)源码分析
- android的消息处理机制(图文+源码分析)
- Android异步消息处理机制详解及源码分析
- Replace INTO和INSERT INTO的区别 - mysql
- 本地目录的只读控制(禁止写、删除、新建))
- MyBatis 动态SQL
- uCOS和uCLinux比较
- ubuntu常用命令操作指南
- Android消息处理-源码分析篇
- VS2010下如何调试Framework源代码(即FCL)
- Android Robotium的自动化代码
- Windows文件在linux下每行后出现^M字样
- 面向对象的三个基本特征
- Java的辉煌,还能延续多久?
- Oracle ORA-600[4193] 解决方法 说明
- java连接DB2
- Linux下库文件学习(一)库的基本知识