Handler是android提供的一种异步回调机制,android UI界面运行于ActivityThread主线程中,UI的刷新只能在主线程进行,主线程不能阻塞,也就是不能做一些超时的操作,例如网络请求。所以一般我们都会开启新的线程来进行网络数据的获取,在获取完成后需要通知UI进行界面的刷新。而UI运行于主线程,需要有个消息机制来通知它进行刷新。而handler就可以用于子线程发送的数据, 并用此数据配合主线程更新UI。
UI主线程中只要定义一个Handler并实现handleMessage方法,该方法能够获取子线程传来的消息,然后在UI主线程进行处理。那么handler是怎么发送消息的呢,主界面又是怎么收到消息的呢。
Android的这种线程间通信机制主要通过下面四个类实现:Hanlder、Looper、MessageQueue,Message,其中Handler是消息的收发器,MessageQueue是一个消息队列,每个线程都有其对应的消息队列,Message是消息类,Looper可以理解为一个循环取消息的线程,它与一个消息队列进行绑定,并设置到当前线程的ThreadLocal中。
我们在主线程中首先会Handler mHandler = new Handler();并实现handleMessage方法。然后当异步线程处理结束后通过调用mHandler.sendMessage(msg)发送一个消息到主线程,该消息会插入到MessageQueue中(queue.enqueueMessage(msg, uptimeMillis)),handler在初始化时,会获取当前线程的Looper对象和MessageQueue队列,代码如下:
public Handler(Callback callback, boolean async) {
..........
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
}
其中Looper.myLooper,用于获取当前线程对应的Looper对象,如下所示:
public static Looper myLooper() {
return sThreadLocal.get();
}
当Looper为空时就会报异常,也就是说我们在子线程中如果不创建Looper对象就会报异常。但我们并没有在UI主线程中使用sThreadLocal.set()方法啊,那么ThreadLocal的Looper对象时什么时候设置进去的呢?原来在ActivityThread,也就是UI主线程中,android给我们进行了默认的设置:
public static void main(String[] args) {
................
Looper.prepareMainLooper();
Looper.loop();
}
在prepareMainLooper方法中就会位当前UI线程设置一个Looper对象,loop用于进入循环的消息处理中。在我们自己定义的子线程中如果想使用Handler,需要采用如下的方式实现:
Loper.prepare();//为当前线程创建一个Looper对象,和MessageQueue
Handler mHandler = new Handler();
Looper.loop();//进入消息循环
在Loop方法主要代码如下,主要用于循环取出MessageQueue队列中的消息,发送到目的地进行处理。
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
msg.target.dispatchMessage(msg);
}
}
可以看出Loop方法首先获取当前线程的Looper对象和对应的MessageQueue,然后循环取出每个消息,通过 msg.target发送消息出去,target其实就是Handle对象,在handle发送消息的时候就已经绑定了的
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}