经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
Handler,Looper,MessageQueue流程梳理
来源:cnblogs  作者:鲨鱼也疯狂  时间:2019/1/14 9:35:03  对本文有异议

 

目的:handle的出现主要是为了解决线程间通讯。

  举个例子,android是不允许在主线程中访问网络,因为这样会阻塞主线程,影响性能,所以访问网络都是放在子线程中执行,对于网络返回的结果则需要显示在主线程中,handler就是连接主线程和子线程的桥梁。

 

1.handler基本使用方法

  看一下使用方法:

  1. public static final int EMPTY_MSG = 0;
  2. @SuppressLint("HandlerLeak")
  3. Handler handler = new Handler(){
  4. @Override
  5. public void handleMessage(Message msg) {
  6. switch (msg.what){
  7. case 0:
  8. Toast.makeText(MainActivitys.this, "接受到消息", Toast.LENGTH_SHORT).show();
  9. break;
  10. }
  11. }
  12. };
  13. @Override
  14. protected void onCreate(@Nullable Bundle savedInstanceState) {
  15. super.onCreate(savedInstanceState);
  16. setContentView(R.layout.activity_main);
  17. new Thread(new Runnable() {
  18. @Override
  19. public void run() {
  20. handler.sendEmptyMessage(0);
  21. }
  22. }).start();
  23. }

  通过上边代码就完成了子线程向主线程发送消息的功能。

 

2. handler,Looper,MessageQueue 解释

  handler:负责发送和处理消息

  Looper:消息循环器,也可以理解为消息泵,主动地获取消息,并交给handler来处理

  MessageQueue:消息队列,用来存储消息

 

3.源码分析

  程序的启动是在ActivityThread的main方法中

  1. public static void main(){
  2. Looper.prepare(); //1
  3. Handler handler = new Handler();//2
  4. Looper.loop(); //3
  5. }

  Looper.prepare()会初始化当前线程的looper

  1. private static void prepare(boolean quitAllowed) {
  2. if (sThreadLocal.get() != null) {
  3. throw new RuntimeException("Only one Looper may be created per thread");
  4. }
  5. sThreadLocal.set(new Looper(quitAllowed));
  6. }

  会调用到sThreadLocal.set()方法,ThreadLocal是线程安全的,不同的线程获取到的值是不一样的,下面先分析一下ThreadLocal是如何做到线程安全。

  1. public void set(T value) {
  2. Thread t = Thread.currentThread();
  3. ThreadLocalMap map = getMap(t);
  4. if (map != null)
  5. map.set(this, value);
  6. else
  7. createMap(t, value);
  8. }

  不同的线程会设置不同的looper,下面看一下ThreadLocalMap是如何存储数据的

  

  1. ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
  2. table = new Entry[INITIAL_CAPACITY];
  3. int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
  4. table[i] = new Entry(firstKey, firstValue);
  5. }

  ThreadLocalMap会创建一个数组,key是通过特殊的算法来创建出来,一个线程中会有一个ThreadLocalMap,这个map中会存多个ThreadLocal和values。

  下面看下ThreadLocalMap是如何set一个值的

  

  1. private void set(ThreadLocal key, Object value) {
  2. // We don't use a fast path as with get() because it is at
  3. // least as common to use set() to create new entries as
  4. // it is to replace existing ones, in which case, a fast
  5. // path would fail more often than not.
  6. Entry[] tab = table;
  7. int len = tab.length;
  8. int i = key.threadLocalHashCode & (len-1);
  9. for (Entry e = tab[i];
  10. e != null;
  11. e = tab[i = nextIndex(i, len)]) {
  12. ThreadLocal k = e.get();
  13. if (k == key) {
  14. e.value = value;
  15. return;
  16. }
  17. if (k == null) {
  18. replaceStaleEntry(key, value, i);
  19. return;
  20. }
  21. }
  22. tab[i] = new Entry(key, value);
  23. int sz = ++size;
  24. if (!cleanSomeSlots(i, sz) && sz >= threshold)
  25. rehash();
  26. }

 

  其实是遍历threadLocalMap中的table,如果当前table中存在threadLocal这个key就更新,不存在就新建。ThreadLocal的set方法到此结束。

 

  下面看下Handler handler = new Handler()中执行了哪些操作:

  1. public Handler(Callback callback, boolean async) {
  2. mLooper = Looper.myLooper();
  3. mQueue = mLooper.mQueue;
  4. }

  重要的就是构造函数中这两个方法,在handler中初始化looper和messageQueue。这个就不展开讲了。

  

 

  下面看一下Looper.loop()这个步骤,我做了一些精简,把无关的代码去掉了。

  1. public static void loop() {
  2. final Looper me = myLooper();
  3. final MessageQueue queue = me.mQueue;
  4. for (;;) {
  5. Message msg = queue.next(); // might block
  6. if (msg == null) {
  7. // No message indicates that the message queue is quitting.
  8. return;
  9. }
  10. msg.target.dispatchMessage(msg);
  11. msg.recycleUnchecked();
  12. }
  13. }

  queue.next()是个无限for循环,其实也是个阻塞方法,其中比较重要的是下面这个方法,其作用是不会一直循环。底层采用的是pipe/epoll机制。

  1. nativePollOnce(ptr, nextPollTimeoutMillis);
  1. Message next() {
  2. // Return here if the message loop has already quit and been disposed.
  3. // This can happen if the application tries to restart a looper after quit
  4. // which is not supported.
  5. final long ptr = mPtr;
  6. if (ptr == 0) {
  7. return null;
  8. }
  9. int pendingIdleHandlerCount = -1; // -1 only during first iteration
  10. int nextPollTimeoutMillis = 0;
  11. for (;;) {
  12. if (nextPollTimeoutMillis != 0) {
  13. Binder.flushPendingCommands();
  14. }
  15. nativePollOnce(ptr, nextPollTimeoutMillis);
  16. synchronized (this) {
  17. // Try to retrieve the next message. Return if found.
  18. final long now = SystemClock.uptimeMillis();
  19. Message prevMsg = null;
  20. Message msg = mMessages;
  21. if (msg != null && msg.target == null) {
  22. // Stalled by a barrier. Find the next asynchronous message in the queue.
  23. do {
  24. prevMsg = msg;
  25. msg = msg.next;
  26. } while (msg != null && !msg.isAsynchronous());
  27. }
  28. if (msg != null) {
  29. if (now < msg.when) {
  30. // Next message is not ready. Set a timeout to wake up when it is ready.
  31. nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
  32. } else {
  33. // Got a message.
  34. mBlocked = false;
  35. if (prevMsg != null) {
  36. prevMsg.next = msg.next;
  37. } else {
  38. mMessages = msg.next;
  39. }
  40. msg.next = null;
  41. if (DEBUG) Log.v(TAG, "Returning message: " + msg);
  42. msg.markInUse();
  43. return msg;
  44. }
  45. } else {
  46. // No more messages.
  47. nextPollTimeoutMillis = -1;
  48. }
  49. // Process the quit message now that all pending messages have been handled.
  50. if (mQuitting) {
  51. dispose();
  52. return null;
  53. }
  54. // If first time idle, then get the number of idlers to run.
  55. // Idle handles only run if the queue is empty or if the first message
  56. // in the queue (possibly a barrier) is due to be handled in the future.
  57. if (pendingIdleHandlerCount < 0
  58. && (mMessages == null || now < mMessages.when)) {
  59. pendingIdleHandlerCount = mIdleHandlers.size();
  60. }
  61. if (pendingIdleHandlerCount <= 0) {
  62. // No idle handlers to run. Loop and wait some more.
  63. mBlocked = true;
  64. continue;
  65. }
  66. if (mPendingIdleHandlers == null) {
  67. mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
  68. }
  69. mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
  70. }
  71. // Run the idle handlers.
  72. // We only ever reach this code block during the first iteration.
  73. for (int i = 0; i < pendingIdleHandlerCount; i++) {
  74. final IdleHandler idler = mPendingIdleHandlers[i];
  75. mPendingIdleHandlers[i] = null; // release the reference to the handler
  76.  
  77. boolean keep = false;
  78. try {
  79. keep = idler.queueIdle();
  80. } catch (Throwable t) {
  81. Log.wtf(TAG, "IdleHandler threw exception", t);
  82. }
  83. if (!keep) {
  84. synchronized (this) {
  85. mIdleHandlers.remove(idler);
  86. }
  87. }
  88. }
  89. // Reset the idle handler count to 0 so we do not run them again.
  90. pendingIdleHandlerCount = 0;
  91. // While calling an idle handler, a new message could have been delivered
  92. // so go back and look again for a pending message without waiting.
  93. nextPollTimeoutMillis = 0;
  94. }
  95. }

   message.next()返回消息之后会接着调用 msg.target.dispatchMessage(msg);在这个方法里边会进行判断,来决定执行哪一种回调。

  

  1. public void dispatchMessage(Message msg) {
  2. if (msg.callback != null) {
  3. handleCallback(msg);
  4. } else {
  5. if (mCallback != null) {
  6. if (mCallback.handleMessage(msg)) {
  7. return;
  8. }
  9. }
  10. handleMessage(msg);
  11. }
  12. }

到此整个handler的流程就结束了。最后附上一张handler的时序图。

 

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号