异步消息处理机制之Handler源码分析篇

来源:互联网 发布:linux开启ftp服务 编辑:程序博客网 时间:2024/06/05 15:39

异步消息处理机制之Handler源码分析

Android中的异步消息处理机制主要由四个部分组成, Message、Handler、MessageQueue和Looper。

1、Message

Message是在线程之间传递的消息,可以携带少量消息。可在不同线程之间交换数据

2、Handler

Handler是处理者的意思,主要的功能用于发送消息和处理消息。发送消息一般使用sendMessage()方法,发出的消息经过处理,最终传到Handler的handlerMessage()方法中处理

3、MessageQueue

MessageQueue是消息队列的意思,主要用于存放经由Handler发送的消息。这写消息将会一直存到消息队列中,等待被处理且每一线程只有一个MessageQueue对象

4、Looper

Looper是每个线程的MessageQueue管家,通过调用Looper()的loop()方法后,就会进入一个无限循环中,如果MessagQueue中有消息就取出,并传递到Handler的handleMessage方法中。每个线程只有一个Looper对象。

将前面的概念进行总结

首先在主线程中创建一个Handler对象,重写handleMessage()方法,然后当子线程需进行UI操作时,就会创建一个Message对象,然后通过Handler的sendMessage()将消息发送出去,这条消息最终将被添加到MessageQueue队列中等待被处理,而Looper将会不断尝试从MessageQueue中取出消息并处理消息,最后分发回Handler的handleMessage()方法中。由于Handler是在主线程创建,那么handleMessage也会在主线程中执行,这样就可以进行UI操作了。

接下来就是进行源码分析了…….

需求? 子线程发送一串字符串到主线程,通过吐司的方式显示(省略了一些代码…请读者自己实现下吧! 代码简单到不好意思!!!)

// 这段代码放在主线程中private Handler mHandler = new Handler() {    public void handleMessage(android.os.Message msg) {        // UI thread        switch (msg.what) {        case 1:            Toast.makeText(MainActivity.this, "" + msg.obj,                    Toast.LENGTH_SHORT).show();            break;        default:            break;        }    };};//这段代码在子线程中    new Thread(new Runnable() {        @Override        public void run() {            Message msg = new Message();            msg.what = SUCCESS;            msg.obj = "我正做着不可描述的事情...";            mHandler.sendMessage(msg);        }    }).start();

疑问一 怎样获取消息的呢?

点击源码进入Message类中可看到obtain()静态方法

    public static Message obtain() {        synchronized (sPoolSync) {            if (sPool != null) {    //判断消息是否为空,不为空则取出头节点,这里是用单链表管理(数据结构的知识哦!!!)                Message m = sPool;   //获取头节点                sPool = m.next;  //头结点指向下一个消息                m.next = null;//将当前取出的消息的next指针域置为空                sPoolSize--; //消息数量减一                return m;//返回取出的节点            }        }        return new Message();    //如果消息队列中没有消息,则直接创建一个新的消息    }

疑问二 怎样发送消息的呢?

 点击sendMessage()进入,然后一直跟下去,最终调用的是sendMessageAtTime(Message msg, long uptimeMillis)方法。第一个参数发送的消息,第二个参数判断是不是及时消息(在后续分析中很有用,暂且不管)

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {        MessageQueue queue = mQueue;//获取消息队列       .... //省略了一部分代码,关于异常处理的        return enqueueMessage(queue, msg, uptimeMillis);  //我们又发现调用了这个方法,接着跟下去,代码如下    }private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {            msg.target = this;  //这里实际上拿到了的是当前Handler对象           ...//省略代码            return queue.enqueueMessage(msg, uptimeMillis);  //我们可以看到这里调用的是MessageQueue的enqueueMessage,接着往下走,代码如下:        }

//哇!!! 一大波代码来袭,这里我挑重点介绍

    boolean enqueueMessage(Message msg, long when) {       ......//省略代码部分            msg.when = when;   //当前消息的时间,即在队列中多久后发出这个消息            Message p = mMessages;//获取消息队列头结点            if (p == null || when == 0 || when < p.when) {//判断是否是及时消息,也就是说是不是需要立马发出去的消息                      msg.next = p;//待发出去的消息的next指针域指向头结点                mMessages = msg;//头结点指向待发出去的消息节点,相当于头插                needWake = mBlocked;            } else { // 那如果不是及时消息呢?  如果when 不等于0, 那么是不是会从消息队列中找合适可以插入的位置呢?                needWake = mBlocked && p.target == null && msg.isAsynchronous();                Message prev;   //保存上一个节点                for (;;) {  //循环遍历消息队列,实际上就是由排序好的单链表管理                    prev = p; //始终保存当前节点的上一个节点位置                    p = p.next;//不断的前进                    if (p == null || when < p.when) {//找到合适的位置,比如 插入的消息when = 3,而当前排序好的队列中有为when=4的消息, 那么就将该消息插入when = 4的前面, break跳出的循环就是建立链表的逻辑                        break;                    }                    if (needWake && p.isAsynchronous()) {                        needWake = false;                    }                }                msg.next = p; // 待插入的消息的next指针域指向当前遍历退出的节点                prev.next = msg;//由之前定义的pre节点的指针域指向待插入的节点,这样就把它链接起来啦            }        }        return true;    }

到这里已将怎样获取消息、怎样发送消息讲解完毕。下面是不是应该分析Looper类的功能了呢?Looper是怎样从MessageQueue中取出消息? 又是怎样分发到Handler的handleMessage中进行处理的呢? 继续往下看!!!

点击Looper类进去查看它做的是什么功能?我们看它的官方描述

Class used to run a message loop for a thread.  Threads by default do  * not have a message loop associated with them; to create one, call  * {@link #prepare} in the thread that is to run the loop, and then  * {@link #loop} to have it process messages until the loop is stopped.  *   * <p>Most interaction with a message loop is through the  * {@link Handler} class.  *   * <p>This is a typical example of the implementation of a Looper thread,  * using the separation of {@link #prepare} and {@link #loop} to create an  * initial Handler to communicate with the Looper.  *  * /

我将这段话总结如下: Looper类有个loop方法可以从无限循环的消息队列中获取消息, 官方给了一个例子如下:

 class LooperThread extends Thread {    //创建子线程      *      public Handler mHandler;      *      *      public void run() {      *          Looper.prepare(); //这个方法是什么呢? 待会儿往下看.......      *      *          mHandler = new Handler() {   //很熟悉吧!我们在主线程中也创建过这个类吧!      *              public void handleMessage(Message msg) {//这个方法就是将取出的消息分发到这儿来处理      *                  // process incoming messages here          * //要处理的消息放在这儿吧!!!!      *              }      *          };      *      *          Looper.loop();  //我们大概说过,loop()方法是无限循环获取消息吧      *      }

官方代码是不是跟我们的有一些区别呢? 我们是在主线程中通过new Handler()处理的消息吧!那这里又有什么区别呢?首先我们需要明确一点,每一线程都有自己的Looper对象, 那么主线程肯定也有自己的Looper对象吧! 继续看源码。查看Looper类中有一个方法prepareMainLooper(),代码如下:

 /** 这段注释尤其重要,它讲述的是:主线程的Looper对象是由Android系统启动而调用的,是不是很复杂呢?  接下来就需要查看系统源码了,先大概抛出了一个类,ActivityThread.java,等会讲解。     * Initialize the current thread as a looper, marking it as an     * application's main looper. The main looper for your application     * is created by the Android environment, so you should never need     * to call this function yourself.  See also: {@link #prepare()}     */ public static void prepareMainLooper() {    prepare(false);    //下面讲解。。。。。   ......//省略代码        sMainLooper = myLooper();  //获取主线程Looper,这里还得继续看源码    }}//返回一个sThreadLocal.get(),这又是什么呢?接着往下看public static Looper myLooper() {        return sThreadLocal.get();    }   static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();    //是不是有点像一个集合呢?根据泛型得知,存储的是Looper对象吧! 其实这就是一个本地线程管理,用来区分当前是哪个线程的Looper对象。  这里是不是应该有疑问? 为什么没有看到怎样存Looper对象的呢?如果你没有存储, 你又是怎样取出来的呢? 我们刚才有个方法没有讲吧!   prepare(false);我们看它的实现方法,代码如下:   private static void prepare(boolean quitAllowed) {   .....//省略代码部分    sThreadLocal.set(new Looper(quitAllowed));  //看到这儿,是不是一切都明了啦,  相当于说这里将当前线程的Looper的对象添加到本地线程管理,  然后通过get方法取出来呢?}

哇!!! 相信看到这人有些哥们已经下车了吧!!!多理解理解吧!! 接下来查看之前抛出来的ActivityThread.java类了。源码如下:

public static void main(String[] args) {        SamplingProfilerIntegration.start();        ....//省略部分代码        Looper.prepareMainLooper();  //是不是看到了呢? 实际上就是当Android系统启动分为两部分,一部分是底层驱动启动, 另外一部分是Android上层应用启动,在这里说明了,主线程的Looper是由系统启动的。        if (sMainThreadHandler == null) {            sMainThreadHandler = new Handler();        }        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }

接下来就是最后一个方法了, Looper类的loop方法到底做了些什么? 不废话,直接上源码:

//看到这么多代码,是不是很燥呢? 接下来我进行细致分析。 public static void loop() {        final Looper me = myLooper(); //获取当前的Looper对象        ...//省略部分代码        final MessageQueue queue = me.mQueue;//获取当前消息队列      ...//省略部分代码        for (;;) {   //重头戏,  这不就是前面所说的死循环从消息队列中获取消息吗?             Message msg = queue.next(); // 获取消息   ...//省略部分代码            msg.target.dispatchMessage(msg);  //终于看到了消息的分发了, 通过Handler的dispatchMessage()将消息分发出去,等会做细致讲解。           ...//省略部分代码            msg.recycle(); //清空消息        }    }/**     * Handle system messages here.     */    public void dispatchMessage(Message msg) {        if (msg.callback != null) {              handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {  //回调handleMessage()这个方法。                    return;                }            }            handleMessage(msg);        }    }

强调一下: 由于主线程的Looper的loop()是在主线程中执行,那么必然有分发消息也是在主线程中执行,再推理,也就是说,最终handleMessage()就是在主线程中执行了!!! 那么就可以进行UI操作了

总结

在主线程中创建了Handler对象,并覆写了handleMessage()方法, 而我们在子线程中创建消息(当然也可以从消息队列中获取复用,我们这里的消息队列数为10个,我就不做讲解了。最终将其发出去),最终被添加到MessageQueue()队列中(按条件插入合适的位置),然后交由当前Looper的loop()方法从MessageQueue()中取出消息。最终分发到handleMessage()中执行

示意图如下

这里写图片描述

0 0