您的位置:首页 > 运维架构

Handler Looper Message 之间的那些事

2015-06-19 15:52 253 查看
本文参考http://blog.csdn.net/lmj623565791/article/details/43452969 hongyang大神的文章… 加入了一些自己的解读…

对于Handler Looper Message 之前一直只是知道理论,知其然不知所以然,看了hongyang大神的源码分析,写个总结帖.

一.概念..

Handler 、 Looper 、Message 这三者都与Android异步消息处理线程相关的概念。

异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。

说了这一堆,那么和Handler 、 Looper 、Message有啥关系?其实Looper负责的就是创建一个MessageQueue,然后进入一个无限循环体不断从该MessageQueue中读取消息,而消息的创建者就是一个或多个Handler 。

二.源码分析

说道Handler也是面试常客了… 当然这也是Android中比较重要的一个部分。

就按他们出场的先后顺序来分析吧(源码基于API 19,建议边看边根据源码对照)

Looper

最先是Looper… 为什么不是Handler? 这个问题和为什么主线程中我们没有调用Looper.prepare()的回答其实是一样的…

…App启动时 已经调用Looper.prepare()方法

那么我们来看看Looper.prepare()是做什么的呢..

public static void prepare() {

prepare(true);

}
private static void prepare(boolean quitAllowed) {

if (sThreadLocal.get() != null) {

throw new RuntimeException("Only one Looper may be created per thread");

}

sThreadLocal.set(new Looper(quitAllowed));

}


如果当前线程当中已经有Looper了 就抛出异常(这也是为什么一个线程只能有一个Looper实例 ,只有一个MessageQueue实例的原因) ;

如果没有就初始化一个Looper对象放入当前线程中

说明一下:sThreadLocal是什么..看源码便知 是用来存放当前当前线程的Looper对象的

static final ThreadLocalsThreadLocal = new ThreadLocal();

再看看Looper的构造方法

private Looper(boolean quitAllowed) {

mQueue = new MessageQueue(quitAllowed);

mRun = true;

mThread = Thread.currentThread();

}


构造方法中,最主要的就是创建了一个MessageQueue,这东西是干嘛用的呢? 存放Handler发来的消息(接下来会证实),所以俗称 消息队列。

继续看Loop()方法 这个就不贴全部源码了 挑重要部分进行分析…(建议参照源码进行阅读)

final Looper me = myLooper();

if (me == null) {

throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this            thread.");

}


final MessageQueue queue = me.mQueue;

拿到当前线程中的Looper实例(如果Looper实例为空就会抛出异常)再去拿Looper中的MessageQueue实例

for (;;) {

Message msg = queue.next();

if (msg == null) {

return;

}

msg.target.dispatchMessage(msg);

msg.recycle();


}

依然只上了关键代码…

一个无限循环 不断从MessageQueue实例中获取下一个Message,未获取到便返回;

获取到Message实例则调用msg.target.dispatchMessage(msg); 把消息交给Message实例的target的dispatchMessage方法处理,看到这里可能会有疑问了,msg的target是什么? 查看源码可以发现就是Handler对象,下面分析Handler的时候会说道;最后释放msg

小结:Looper的主要作用

1.与当前线程绑定,并保证一个线程中只有一个Looper实例,从而保证了一个线程中只有一个MessageQueue实例。

2.loop()方法,就是不断的从MessageQueue中取出消息,并交给msg.target.dispatchMessage(msg)去处理(交给Handler去处理,接下来会细说)

Handler

我们用Handler之前都会初始化一个Handler对来更新UI线程…等等. 上源码. 来看看Handler是如何与MessageQueue关联上的 ,还有在非UI线程中是如何将消息发送到MessageQueue当中去的

来看看Handler的构造方法

public Handler(Callback callback, boolean async) {

// 省略相关检查代码

mLooper = Looper.myLooper();     // Tag1

if (mLooper == null) {

throw new RuntimeException(

"Can't create handler inside thread that has not called Looper.prepare()");

}

mQueue = mLooper.mQueue;      //Tag2

mCallback = callback;

mAsynchronous = async;

}


Tag1 调用了Looper.myLooper() 前面Looper分析环节说到了 这是从当前线程中拿出Looper实例

Tag2 将Looper实例中的MessageQueue实例取出来

此时,Handler和MessageQueue就关联上了

辣么? 我们我们在非UI线程中,是如何将消息传递给MessageQueue的呢… 继续看源码

平时通过Handler发送一条Message是不是通过

public final boolean sendMessage(Message msg){

return sendMessageDelayed(msg, 0);

}
public final boolean sendMessageDelayed(Message msg, long delayMillis){

if (delayMillis < 0) {

delayMillis = 0;

}

return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);

}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {

MessageQueue queue = mQueue;

if (queue == null) {

RuntimeException e = new RuntimeException(

this + " sendMessageAtTime() called with no mQueue");

Log.w("Looper", e.getMessage(), e);

return false;

}

return enqueueMessage(queue, msg, uptimeMillis);

}


看到这里,大家想起了什么? 没错 mQueue就是Handler初始化的时候 从当前线程取出的Looper中取出来的MessageQueue

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {

msg.target = this;

if (mAsynchronous) {

msg.setAsynchronous(true);

}

return queue.enqueueMessage(msg, uptimeMillis);

}


msg.target = this; ?? msg.target赋值为this,(大家还记得在前面Looper.loop()方法中不断从消息队列中读消息 然后调用 msg.target.dispatchMessage(msg)来处理信息 ) ;也就是将当前Handler实例作为msg的target属性,最终调用了queue.enqueueMessage(msg, uptimeMillis)方法 将消息存入MessageQueue中去 ; 换一句话说 Handler发出来的消息 最终保存到了MessageQueue中去

现在消息已经存入了MessageQueue中,前面提到Looper.loop() 会不断的读取MessageQueue中有无信息,有信息会通过msg.target(也就是Handler) 来调用dispatchMessage(msg) ,来看看源码

/**

* Handle system messages here.

*/

public void dispatchMessage(Message msg) {

if (msg.callback != null) {

handleCallback(msg);

} else {

if (mCallback != null) {

if (mCallback.handleMessage(msg)) {

return;

}

}

handleMessage(msg);

}

}


最后调用了handleMessage(msg); 看到这里是不是感觉很熟悉了 我们再快看看handleMessage(msg)的源码

public void handleMessage(Message msg) {

}


里面什么都没有? 什么情况? 回想一下,平时我们初始化一个Handler实例 是不是都会复写handleMessage方法? 因为最终回调是由我们来控制。说道这里 整个异步消息处理的流程已经梳理完了 让我们来总结一下

1、首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。

2、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。

3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。

4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。

5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。

内容参考Hongyang大神的博客… 概念和总结属于搬运工… 源码分析属于二次加工,加上了一些自己的理解… 今天正好代码重构完成… 忙里偷闲写了这篇总结帖… 希望对大家在学习Android的道路上 有所帮助…
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息