Handler机制初探

Posted by boredream on March 16, 2016

总的来说就是给线程绑定一个轮询器Looper和一个消息队列MessageQueue, 然后用Handler给当前线程对应的MessageQueue发送消息,然后由Looper去loop遍历消息队列, 让消息排着队按照规则挨个执行。 特殊的是安卓中有一个主线程Looper,更新UI的操作只能在这个主线程中执行。


Looper即轮询器,用于loop轮循遍历线程中的MessageQueue里的Message进行处理。

程序启动的时候会有一个ActivityThread,相当于程序的入口 从其中的main函数就可以看出来,是进入整个程序的大门

main函数中会调用一个Looper.prepareMainLooper() 方法 作用是把当前的线程初始化成一个Looper,然后将其作为整个程序的主线程轮询器。 初始化创建Looper的时候还会新建一个对应的消息队列MessageQueue, main函数里创建完主线程Looper后还会让其调用下loop方法,开始对消息队列开始轮循遍历处理

开发者也可以在子线程中自行创建Looper,子线程中只要调用了prepare, 都会新建一个和当前线程相关联的Looper对象,同时还会将其保存到一个集合中。 但这些都属于非主线程Looper了。

Looper类里面有一个主线程Looper,一个Looper集合,代码如下

1
2
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // guarded by Looper.class

注意: 子线程即使prepare初始化成了一个Looper也不能更新UI,只有主线程Looper才可以。


Handler类 首先是新建Handler,无参数的构造方法中,会先用Looper.myLooper()方法获取当前线程的Looper, 这个myLooper方法其实就是从sThreadLocal集合中获取当前线程对应的Looper对象。

如果是在Activity中直接创建Handler,那么获取到就是sMainLooper即主线程对应的轮循器。 而如果在子线程中去new Handler(),则会直接报错 “Can’t create handler inside thread that has not called Looper.prepare()” 因为子线程需要先prepare为其初始化一个Looper,然后才能在这个线程中去new Handler。

Handler的使用,一般是post一个runnable或者send一个message post runnable实质上也是send message,只不过把这个runnable作为了message的callback参数, 最终都会调用sendMessageAtTime方法。

这个方法会先获取当前Handler的MessageQueue 消息队列,这个队列是Looper创建时新建的。 即当成功创建一个Handler时,还会同时有一个Looper和一个MessageQueue。

获取到队列后就会调用MessageQueue的.enqueueMessage方法,把需要发送的消息加入到队列中~ 主线程Looper在新建后又调用了一个loop方法,这个方法就是获取looper对应的消息队列,然后不停的去循环获取其中的消息,进行处理,将其dispatch分发给handler目标,然后判断新建Handler时是否传入了callback,如果有则让这个回调处理消息,如果没有就调用handleMessage消息。

一般使用最多的也就是这个handleMessage方法


MessageQueue消息队列 基本属于一个容器,用队列先进先出的规则保存Message消息。 每个Looper都会对应有一个,然后轮循器会调用loop方法遍历消息队列分发处理其中的消息 。


注意,Handler如果在子线程里使用,一定要先Looper.prepare(),然后调用loop开始轮循, 最后不用的时候不要忘了quit退出停止~ 主线程的Handler系统都处理好过了,不用我们考虑太多;子线程的实际使用场景中用的也不多, 因为子线程不能更新UI控件。

那么学这个原理有啥用的~ 学习的时候可能都知道子线程不能更新主线程控件,那么原理呢? 所以我们知道了Handler内部判断是否为主线程主要是看Looper~ 然后又注意到Handler有个构造方法是直接传入Looper的~ 那么就可以在子线程中去new Handler(Looper.getMainLooper()) 这样创建一个主线程对应的handler了~

比如最经典的网络请求,一般是子线程中处理~ 直接自定义回调接口,其实也是在子线程中执行。 想更新主线程一般还要用Handler去发送下消息,特别麻烦~ 这个时候就可以子线程里new Handler(Looper.getMainLooper())获取主线程Handler,然后再去处理就行了~ Volley啊这样的网络请求回调,还有EventBus啊这样的子线程里回调主线程方法都是这个原理。系统自带的AsyncTask内部的回调处理也是这个原理。