朝花夕拾回过头来做整理:Handler

Posted andy-songwei

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了朝花夕拾回过头来做整理:Handler相关的知识,希望对你有一定的参考价值。

      android中子线程和UI线程(即主线程),就想古时候的痴男和怨女。某天,一痴汉看上了一妹子,一见钟情,想要表达爱意和提亲。可是,那个时代,即使再心急火燎的,也不能冒冒失失直接去找妹子求婚啊。怎么办呢?痴汉顺理成章想到了媒婆——Handler!稍有一点Android开发基础的朋友都知道“主线程不做耗时操作,子线程不更新UI ”。可是当子线程完成耗时操作,需要更新UI,要怎么办呢?Handler作为一名经验老到的媒婆,在痴汉(子线程)和妹子(UI线程)之间扮演了总要的信使角色。

1. 痴汉委托媒婆送情书。

                                技术分享图片

             ① sendMessage(Message msg)源码截图

                  技术分享图片

             ② sendEmptyMessage(int what)源码截图

                  技术分享图片

                  技术分享图片

             ③ post(Runnable r) 源码截图

                  技术分享图片

                  技术分享图片

             ④ postDelayed(Runnable r, long delayMillis)源码截图

                  技术分享图片

       看,多么熟悉的面孔!!!没错,sendMessage(...),sendEmptyMessage(...), post(...), postDelay(...),平时使用最广泛的四个方法,媒婆Handler主要就是靠的这四招,替子线程向UI线程递信(发送Message)的。从上图中左上角我们可以直观地看到,这四个方法都直接或间接递地在调用sendMessageDelay(...)方法。殊途同归,Handler无论使用何种方法,其宗旨都是在想办法传递信息。无疑,Handler这个媒婆很聪明,会根据不同的场景选择使用其中的一种合适的方法。

         

       2.媒婆把情书存入信箱。

         sendMessageDelay(...)层层往下走,会调用enqueue(...)方法。该方法会触发MessageQueue调用自己的enqueueMessage(...)方法。

          技术分享图片

          MessageQueue中enqueueMessage(...)方法的关键代码如下:

          技术分享图片

           MessageQueue,就像妹子的信箱,媒婆(Handler)送来的情书(Message)就被存储这个里面。它的学名叫做消息队列,从源码总可以看到,它采用链表的形式,无限循环将消息加入其中。

 

        3. 妹子派丫鬟取情书。

        情书总是不期而至,媒婆把情书放入信箱,却没有通知妹子,妹子又如何知道意中人有信件送达呢?原来,妹子是大家闺秀,专门有丫鬟伺候她,时时刻刻检查着信箱。这个丫鬟就叫Looper,扮演着重要的角色。

        Looper中有个loop( )方法,正如以下代码所示,本质上是调用MessageQueue.next( )方法。这个方法可以对应MessageQueue.enqueueMessage(...)方法,一个无限循环存入Message,另一个就无限循环取出Message。就是Looper这个勤劳的丫鬟在不停查看信箱,有情书到了,就取出来交给妹子。如下代码显示了Looper从消息队列中取消息的关键代码,可以看到这是一个无限循环的过程。这里,要着重注意第14行, msg.target在前面的enqueueMessage(...)方法中有提到,"msg.target = this", 说明它就是Handler的一个实例,对应UI线程中new的Handler实例,它调用了dispatchMessage(msg),该方法的作用就是安排处理Message,后面(妹子读情书)中会详细讲到。

           

 1 public static void loop() {
 2         final Looper me = myLooper();
 3         if (me == null) {
 4             throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread.");
 5         }
 6         final MessageQueue queue = me.mQueue;
 7         ...for (;;) {
 8             Message msg = queue.next(); // might block
 9             if (msg == null) {
10                 // No message indicates that the message queue is quitting.
11                 return;
12             }
13            ...try {
14                 msg.target.dispatchMessage(msg);
15                 ...
16             } finally {
17                 ...
18             }
19             ...
20             msg.recycleUnchecked();
21         }
22     }

         

       那么问题来了,Looper又是从哪里来的呢?难道是和孙猴子一样从石头中蹦出来的?Android程序本质上也是java程序。我们总是疑惑,为什么我们的app中怎么也找不到main( ) 入口?哈哈,其实仍然是有main( )入口函数的,在ActivityThread中:

   

1 public static void main(String[] args) {
2         ...
3         Looper.prepareMainLooper();
4         ...
5         Looper.loop();
6         ...
7     }

       豁然开朗了吧,Looper的身世都在main(...)方法中!Looper.prepareMainLooper()作用是生成一个Looer实例,然后就Looper就调用了loop( )方法,开启了她作为丫鬟勤劳的一生。

      只能说妹子的命是真好,有个好爹,她从出生开始,老爹就为她安排好了体贴的丫鬟,全心全意地为她服务着。

    4.妹子读情书。

      上一节 ”妹子派丫鬟取情书”中提到了该方法。此刻,情书已经到了妹子的手中, 妹子要开始享受地读情书了。

      技术分享图片

      

      5.妹子的反应。

         

以上是关于朝花夕拾回过头来做整理:Handler的主要内容,如果未能解决你的问题,请参考以下文章

学弟学妹提问:曾经对我帮助最大的是什么?我回过头来想了想我的测试历程...

轻松实现状态栏颜色变化

如何在 NIST PIV 卡上实现 VERIFY 命令?

Android R文件相关问题

金蝶handler中 collection 代码片段理解

mybatis的那些事