handler机制可以说是android面试时必考的一项,其思想还是比较nb的,最近和组内的ios同事聊天时,发现ios也有一套类似的机制,可见handler机制的重要性。在了解过后,做一个简单的总结,加深自己的印象。
本文知识点参考以下文章:Android进阶——Android消息机制之Looper、Handler、MessageQueen
一、handler机制相关概念
在聊handler机制之前,我们要先明确一些概念。
1、主线程(UI线程)
当程序第一次启动时,android会同时启动一条主线程(main thread),主线程主要用于处理UI相关事件
2、Message(消息)
handler接收和处理的对象,通过获取message中携带的信息,做相关处理
3、ThreadLocal
线程内部数据存储类,负责存储和获取本线程的looper
4、Message Queue(消息队列)
采用单链表的数据结构来存储消息列表,用来存放handler发出的message,遵循先进先出原则
5、handler(处理者)
Message的主要处理者,用来发送message和处理looper取出的message
6、Looper(循环器)
是MessageQueue和handler之间的桥梁,从MessageQueue取出消息给handler
二、Looper源码解析
主线程在activity启动时就创建好了,启动时回调用looper里的两个主要方法,prepareMainLooper和loop方法。
可以看到,prepareMainLooper方法调用了prepare方法,并且调用后会检测sMainLooper是否为空,如果不为空则会抛异常。为空则通过myLooper方法来获取looper。而同时,prepare方法先检测了sThreadLocal里是否有looper,有则抛异常,没有则新set一个looper。可见在这个过程中,要求sThreadLocal里是不允许有looper的,需要新建一个。从myLooper方法中可以看出,sMainLooper的looper正是取的新创建的looper。
再看looper的构造方法,发现在里面new了一个消息队列,同时把当前线程作为数据持有。这张图里的这几个成员变量应该就是几个核心变量了。一个looper中,持有主looper,looper线程池,消息队列以及当前线程。
对于looper线程池,还要看下面这些源码。
这是ThreadLocal的源码,泛型T接收的就是looper。通过这两个关键方法我们可以看出,从looper线程池里获取loopera时,通过get方法。而ThreadLocal本身维护一个map,以线程为key,looper为value。当该线程作为key,无法从map中取出looper时,会调用setInitialValue方法,以该线程作为key,存放一个新建的value。而这个get方法告诉我们,ThreadLocal是通过这种方式来维持每个线程都有一个自己独立的looper。
三、Looper循环
老规矩,先上图,loop循环的源码比较长,截了两张图
可以看到,loop方法一开始先获取looper,之后获取looper中的message queue。之后是一个for无限循环,不断地从message queue中取消息。使用message queue中的next方法。
而MessageQueue中的next方法是取出自己维护的message链表的第一个,然后将后续的message在排列。
同时我们注意到,取出的msg,会调用其自己target中的dispatchMessage方法。这个target,很多人应该想到,其实就是handler。handler与msg的绑定,是在handler发送msg时最终调用enqueueMessage时绑定的。而handler在创建时,就会通过Looper.mylooper方法获取当前线程的looper。而looper里又持有messageQueue。这样一切就都联系起来了。
四、总体流程
当我们创建一个handler时,会调用looper的mylooper方法来获得当前线程的looper。如果没有,就会新建一个looper并存放在looper线程池里,保证每个线程都有自己唯一的looper。而looper中持有MessageQueue,当新建一个Message时,通过handler发送时,会将这个Message存放到MessageQueue中,而loop方法一直在循环从messageQueue中取消息,发送的message会被取到,并处理。当发送了多个message时,会遵循先进先出原则维持在messageQueue中。那么,不同线程中发送的message,最终是怎么在主线程中完成处理的呢。要从target的dispatchMessage方法中找答案。
五、主线程更新
这是handler中dispatchMessage的源码,可以看到dispatchMessage的处理有三个优先级,先处理handleCallback方法,如果没有实现callback,则执行mCallback里的handleMessage方法,如果mCallback为空,则最终执行handler的handleMessage方法。这个方法在上面,一看竟然为空。我们恍然大悟,原来,这个不是我们在创建handler时就重写的方法吗。
这下一切就都说得通了,handler是在主线程中创建的,其实现方法也是主线程中完成的,我们的message绑定的handler是主线程中的,所以自然处理时就会到主线程中执行。那么有没有在线程中创建的handler的情况呢。有人试过会报错。不过我想通过主线程中的handler已经可以帮我们解决大部分问题了,我们只要在子线程中回调方法即可实现主线程刷新。
六、loop无线循环原理
如果一个looper无线循环,不管它是主线程中还是子线程中,都会造成资源的浪费,以及耗时。那么为什么loop方法能够一直循环呢。是因为loop在取不到消息时,会进入阻塞状态,该状态不会占用资源。这部分后续应该单独开一个线程的专题来详细解答。
好了,本篇handler机制写到这里就结束了,相信读者对于handler机制及流程,应该有一个大致的了解了。