码迷,mamicode.com
首页 > 移动开发 > 详细

Android异步消息处理 Handler Looper Message关系源码分析

时间:2015-09-24 22:54:21      阅读:351      评论:0      收藏:0      [点我收藏+]

标签:

# 标签: 读博客


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

一.概念..

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

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

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

二.源码分析

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

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

Looper

最先是Looper... 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 ThreadLocals<Looper>ThreadLocal = new ThreadLocal<Looper>();

再看看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的道路上 有所帮助...

-

补充,原文基本上说清楚了架构,然而参考代码是4.3并且谈东西没有图,也米有详细代码。

参考即可。

Android异步消息处理 Handler Looper Message关系源码分析

标签:

原文地址:http://my.oschina.net/wizardmerlin/blog/510873

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!