之前一直只知道handler如何使用,不知道其中的工作原理,趁着新版本提测阶段比较空闲,及时做一个总结。
先看一下Google官方文档关于handler的解释:
A Handler allows you to send and process Message
and Runnable objects associated with a thread's MessageQueue
. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
巴拉巴拉一大堆,还好没有什么生僻词大概还能看懂个意思,大概意思就是:handler类允许你发送消息和处理线程消息队列中的消息及runnable对象。handler实例都是与一个线程和该线程的消息队列一起使用,一旦创建了一个新的handler实例,系统就把该实例与一个线程和该线程的消息队列捆绑起来,这将可以发送消息和runnable对象给该消息队列,并在消息队列出口处处理它们。
handler类有两种主要用途:1、按照时间计划,在未来某时刻,对处理一个消息或执行某个runnable实例。 2、把一个对另外线程对象的操作请求放入消息队列中,从而避免线程间冲突。
第一个就是执行一个定时任务(可以立刻执行,比如子线程刷新ui),第二个就是异步消息处理机制。
将通俗一点就是1.更新ui 2.实现消息的异步处理
Part1.更新ui:
有的时候在我们需要执行一些耗时的操作,然后执行完了给一个反馈,如上代码开启了一个子线程去下载lol,然后下载完成后提示下载完成,当程序执行到L:64的是后程序崩溃了,报错原因是只有在主线程才能修改视图,L:65-L:70就是一种handler的典型用法,在子线程post出一个runnable对象,还有其他的方式,比如通过sendMsessage(其实post的内部也是实现sendmessage,ps:message的获取方式Android推荐Message.obtain而不是直接new),L:77和L:79是一种消息不带数据的和一种消息带数据的传递,看一下最终的打印信息:
可以看出uiHandler 接收信息处理信息的threadId和主线程的threadId是一样的,因为handler是在主线程中创建的(L:37),所以uiHandler会把消息传递到主线程,然后再主线程进行最终的ui刷新。不过消息是怎么传递处理的呢?说到handler的消息处理,就不得不提Looper、MessageQueue、Handler、Thread这四者之间的关系了,先上一张网上盗的图:
Thread是最基础的,Looper和MessageQueue都构建在Thread之上,Handler又构建在Looper和MessageQueue之上,我们通过Handler间接地与下面这几个相对底层一点的类打交道。
MessageQueue
讲messageQueue之前插播一下message
Message:包含描述和任意数据对象的消息,用于发送给Handler。
message对象很简单,两个int型的数据类型arg1和arg2是msg可以携带的简单数据,what代表msg的消息码,可以理解成让handler接收时明白你是哪一条消息,obj是Object类型的数据,值得注意的是当你arg0和arg1可以满足msg的内容的时候建议使用arg0和arg1,效率要比使用obj对象来的高的多。
MessageQueue:消息队列。内部存储着一组消息。对外提供了插入和删除的操作。MessageQueue内部是以单链表的数据结构来存储消息列表的。
每一个线程内部都维护了一个消息队列,在某一时刻我们点击了ui界面上的某一个按钮,然后有恰巧收到了程序的广播事件,这个时候该如何处理这两个事件呢?因为同一时刻只能处理意见事情,所以Android把点击按钮封装成了一个message,放到messagequeue里面,收到广播也封装成一个message,放到messagequeue,俗称入栈到消息队列,messageQueue就是一堆消息的消息池,线程Thread会一次从消息队列中取出消息进行处理。message按照“先进先出”的原则存放消息,存放并非实际意义的保存,而是将Message对象以链表的方式串联起来的。MessageQueue对象不需要我们自己创建,而是有Looper对象对其进行管理,一个线程最多只可以拥有一个MessageQueue。
Looper
Looper又称消息泵,主要作用是与当前线程创建一个绑定关系,同时创建一个messageQueue,默认情况下开启一个新的线程是不会开启消息循环的(主线程除外),而在子线程中如果想要new Handler()的话需要先调用Looper.prepare(),然后通过Looper.loop()让Looper开始工作,从消息队列里面取消息,将消息发送给对应的处理者(handler),让messageQueue中的消息循环起来。