Android异步消息处理机制(2)源码解析

上一章讲解了Android异步消息处理机制的基本使用,下面将简单地探寻一下异步机制背后的奥妙,源码版本为:API22。

首先,声明一下本文是在我参考了一下各位大神的文章之后才慢慢熟悉的, 若有不足之处,还望各位批评指正!。菜鸟上路,,,,

郭霖博客

鸿洋博客

刘超 深入解析android5.0系统

任玉刚博客

先后顺序按照拼音排序,无关技术本身。

先简单地总结一下Looper,MessageQueue,Message和Handler四者之间的关系:

  • Looper和MessageQueue

    Looper对象是线程的消息循环处理器,每个线程只能有一个Looper。Looper内部有一个消息队列MessageQueue对象,所有该线程的消息都存放在该队列(按照时间排序)中。android在启动时为主线程(UI线程)自动创建一个Looper对象,而我们自己创建线程时必须要创建Looper对象(调用Looper.prepare())。

  • Handler(非抽象类)

    Handler对象是Message对象的接收者和处理者。用户通过Handler把消息添加到消息队列,同时通过Handler的回调方法hanldeMessage()处理消息。Hanlder在构造时和一个Looper对象关联在一起。Handler和Looper是多对一的关系,多个Handler对象可以和同一个Looper对象建立关系,反之则不行。

  • Message

    Message是消息的载体,是Parcelable的派生类。

四者涉及到的主要成员变量和方法

Looper类的主要成员变量和方法:


public final class Looper {
    // 成员变量
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;
    final MessageQueue mQueue;
    final Thread mThread;
    // 成员方法
    public static void prepare() {...}
    private static void prepare(boolean quitAllowed) {...}
    public static void prepareMainLooper() {...}
    public static Looper getMainLooper() {...}
    public static void loop() {...}
    public static Looper myLooper() {...}
    public static MessageQueue myQueue() {...}
}

MessageQueue类中的主要成员方法:


public final class MessageQueue {
    Message next() {...}
    boolean enqueueMessage(Message msg, long when){...}
}

Handler类中的主要成员变量和方法:

public class Handler {
    //内部接口
    public interface Callback {
        public boolean handleMessage(Message msg);
    }
    //处理消息相关方法
    public void handleMessage(Message msg) {}
    public void dispatchMessage(Message msg) {...}
    private static void handleCallback(Message message) {...}
    // 构造器相关方法
    public Handler() {...}
    public Handler(Callback callback) {...}
    public Handler(Looper looper) {...}
    public Handler(Looper looper, Callback callback) {...}
    public Handler(boolean async) {...}
    public Handler(Callback callback, boolean async) {...}
    public Handler(Looper looper, Callback callback, boolean async) {...}
    // 获取Message相关方法
    public final Message obtainMessage(){...}
    public final Message obtainMessage(int what){...}
    public final Message obtainMessage(int what, Object obj){...}
    public final Message obtainMessage(int what, int arg1, int arg2){...}
    public final Message obtainMessage(int what, int arg1, int arg2, Object obj){...}
    // post相关方法
    public final boolean post(Runnable r){...}
    public final boolean postAtTime(Runnable r, long uptimeMillis){...}
    public final boolean postAtTime(Runnable r, Object token, long uptimeMillis){...}
    public final boolean postDelayed(Runnable r, long delayMillis){...}
    public final boolean postAtFrontOfQueue(Runnable r){...}
    // send相关方法
    public final boolean sendMessage(Message msg){...}
    public final boolean sendEmptyMessage(int what){...}
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {...}
    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {...}
    public final boolean sendMessageDelayed(Message msg, long delayMillis){...}
    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {...}
    public final boolean sendMessageAtFrontOfQueue(Message msg) {...}
    // 进出消息队列
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {...}
}

Message类中的主要成员变量和方法:


public final class Message implements Parcelable {
    // 主要成员变量
    public int what;
    public int arg1;
    public int arg2;
    public Object obj;
    public Messenger replyTo;
    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;
    // 主要成员方法
    public Message() {} // 不建议使用
    // 获取Message相关方法
    public static Message obtain() {...}
    public static Message obtain(Message orig) {...}
    public static Message obtain(Handler h) {...}
    public static Message obtain(Handler h, Runnable callback) {...}
    public static Message obtain(Handler h, int what) {...}
    public static Message obtain(Handler h, int what, Object obj) {...}
    public static Message obtain(Handler h, int what, int arg1, int arg2) {...}
    public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {...}

    public void setTarget(Handler target) {...}
    public void sendToTarget() {...}
    public Handler getTarget() {...}
}

下面将以问答的方式解析异步消息处理机制。

问答解析

1. ThreadLocal的作用?

解析:ThreadLocal是Thread Local Variable即线程本地变量的意思,通过把数据放在ThreadLocal中就可以让每个线程创建一个该变量的副本,从而避免并发访问的线程安全问题。这里保存Looper类的实例对象。

2. 为什么在UI线程中实例化Handler并不需要Looper.prepare(),而在子线程中则需要Looper.prepare()

解析:这是因为在ActivityThread类中的main()方法调用了Looper.prepareMainLooper(),简单代码如下:


public static void main(String[] args) {
       ...// 前面的省略了

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

可以看到main()方法中调用了Looper.prepareMainLooper(),而Looper.prepareMainLooper()源码为:


  public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            // 若已经存在Looper对象直接从ThreadLocal对象sThreadLocal中获取
            sMainLooper = myLooper();
        }
    }

prepare(boolean)代码如下:


 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));
    }

到这里我们可以看到main()方法中调用了Looper.prepareMainLooper(),而Looper.prepareMainLooper()又调用了prepare(boolean)prepare(boolean)方法就会创建一个Looper对象并保存在静态变量sThreadLocal(ThreadLocal类实例)。我们来看一下Looper的构造器:


private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

该构造器为私有的,且创建了一个消息队列实例,并将当前线程保存下来。

同时在prepare(boolean)可以看到若sThreadLocal已经有一个Looper对象后再创建就会出现异常。这说明了两点:1. 一个线程中只能有一个Looper对象;2. 一个线程中只能最多调用一次prepare()等prepare相关的方法(prepare()prepare(boolean)prepareMainLooper()(UI线程中调用))。

而我们在子线程中调用的是Looper.prepare(),来看一下这个方法的源码:


public static void prepare() {
        prepare(true);
    }

可以看到同样也是调用了prepare(boolean)

3. 实例化Handler系统为我们做了哪些事情?

解析:该问题主要涉及到Handler中以下几个方法:


public Handler() {this(null, false);}
public Handler(Callback callback) {this(callback, false);}
public Handler(Looper looper) {this(looper, null, false);}
public Handler(Looper looper, Callback callback) {this(looper, callback, false);}
public Handler(boolean async) {this(null, async);}
public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can‘t create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

在上述构造器中可能传递的参数为:

  • Looper对象,用于指定Handler将消息发送到的Looper对象。因此在实例化Handler对象之前需要调用Looper.prepare()方法。
  • Callback接口对象,用于处理Handler发送的消息。
  • boolean值,是否是异步操作。针对Handler来说这里都是传递的false。

首先,在Handler(Callback callback, boolean async)中我们看到调用了Looper.myLooper()返回Looper对象,若没有则会抛出异常。

根据以上构造器,我们能够有多种方式实例化一个handler对象,前五个构造器最终都会调用下面两个构造器。对于Handler(Callback callback, boolean async)这个构造器没有指定Handler的Looper对象,则使用当前线程的Looper对象。同时我们看到传递了Callback接口对象,将其值赋值给了mCallback,我们来看一下相关的源码:


public interface Callback {public boolean handleMessage(Message msg);}
final Callback mCallback;
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

通过上述代码我们可以看到,若传递Callback接口对象的话,在dispatchMessage(Message msg)中将不会执行handleMessage(msg)(当然这之前先判断msg.callback是否为空,这个问题之后再讲),而是调用mCallback.handleMessage(msg),而该方法就是我们传递的Callback接口对象实现的方法。因此,我们也可以通过这种方式处理消息。若不传递Callback接口对象的话,那么就会执行handleMessage(msg),我们可以通过继承Handler实现该方法处理消息。

4. 实例化Handler对象之后,为什么要调用Looper.loop()?

为了解决这个问题我们先看一下loop()源码:


 public static void 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;
        .....
        for (;;) {
            // 取一条消息,没有就会阻塞
            Message msg = queue.next(); // might block
            if (msg == null) {
                // msg等于null,就会退出
                return;
            }
            .....
            msg.target.dispatchMessage(msg);
            .....
            msg.recycleUnchecked(); // 保证消息循环利用。
        }
    }

loop()中首先获取Looper对象me,然后根据该对象获取对应的消息队列queue,下面就进入一个for(;;)死循环不断地从queue取消息,若没有则next()(该方法比较复杂,我没怎么看懂,,,)方法就会阻塞。

若消息为空的话就会退出loop();下面有一句:msg.target.dispatchMessage(msg);,这个消息msg的target属性就是Handler对象,那么这个消息就是由该Handler来处理的。我们可以通过Message的重载方法obtainXXX()setTarget()设置Handler对象。

通过loop(),就能实现不断处理发送过来的消息。

我们再来看一下Message的sendToTarget():


 public void sendToTarget() {
        target.sendMessage(this);
    }

可以看到通过这个方法发送消息和通过Handler发送消息一样。

5. Handler中的sendMessage()是如何将消息发送到消息队列中的,而消息队列又是如何管理消息的?

首先,看一下,send相关方法:


// send相关方法
public final boolean sendMessage(Message msg){...} // 发送的消息希望及时处理,但不插队
public final boolean sendEmptyMessage(int what){...} // 只发送带有what属性的消息,希望消息及时处理即可
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {...} // 希望延迟处理
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {...} // 希望消息在指定的时间处理
public final boolean sendMessageDelayed(Message msg, long delayMillis){...} // 希望延迟处理
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {...} // 希望消息在指定的时间处理
public final boolean sendMessageAtFrontOfQueue(Message msg) {...} // 消息插队,希望马上处理

带有Empty的方法甚至发送的不是消息,只是what!!注意一下。

上面的send方法,除了sendMessageAtFrontOfQueue(Message),都会调用sendMessageAtTime(Message,long)。而这两个方法中则会返回消息队列的enqueueMessage(Message,long)的方法,该方法就是将消息按照时间顺序插入到消息队列中。其实,消息队列是用链表实现的,排序方式是按照时间进行排列的。enqueueMessage(Message,long)源码如下:


boolean enqueueMessage(Message msg, long when) {
       ....
        synchronized (this) {
           ....
            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }
            ....
        }
        return true;
    }

可以看到首先通过if判断插入的消息的时间是否最小,若是的话直接插入到链表头部,否则进入for (;;)依次遍历链表直到时间小于链表值,然后再插入。消息队列以mMessages为表头进行保存。

以上方法就通过Handler的send方法将消息插入到消息队列中。此时还是在同一个线程中。

6. 线程之间是如何转换的?

  1. 在A线程中实例化Handler对象之后,调用loop()方法循环检测是否有消息进入队列,若有则取出并处理;
  2. 在B线程中,通过Handler对象的sendMessage()方法发送消息,发送过来的消息通过一系列的处理进入到消息队列,这样消息就进入了A线程中。

通过上述两步实现了A线程和B线程之间的通信。

在哪个线程中,又是如何取消息的,怎么阻塞的?

7. 创建消息的方式

创建消息的方式有很多种,我们来对比一下:

  • Handler的obtainMessage()系列;
  • Message的静态方法obtain()系列;
  • 使用构造器创建。

Handler中的方法实际上都是调用Message的静态方法obtain()系列,而obtain()系列最终都会调用obtain()方法。我们来看一下它的源码:


public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

可以看到当前存在多余的消息时,则我们可以直接使用,而不是再创建一个新的消息,循环使用消息减少资源浪费。能够循环使用消息的原因是:每当处理完一个消息时都会调用消息的recycleUnchecked()该方法就会将使用过的消息进行保存。源码如下:


 void recycleUnchecked() {

        ... // 清空消息内容

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

sPool是静态成员变量,保存链表的头,当处理过一个消息时,(消息池大小小于给定值50时)该消息就会采用头插法插入链表中。

8. Handler的post方法是干什么用的?

我们首先回过头来看一下dispatchMessage()方法,其中有一个判断:


if (msg.callback != null) {
    handleCallback(msg);
}

只有当msg.callback为空时才会执行下面的。消息的callback属性是Runnable变量,那么该属性是在哪里赋值的呢?我们发现在post系列中都会调用一个方法getPostMessage():


private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

可以看到该方法可以对callback属性进行赋值,也就是说,通过post方法发送消息的话和send一样可以完成处理消息(实际上在post中也是调用send方法),只不过不在handleMessage()中处理消息,而是在handleCallback()中处理消息。该方法的源码会让我们大吃一惊的:


private static void handleCallback(Message message) {
        message.callback.run();
    }

直接调用了Runnable对象的run()方法,这样一来就不是创建新线程,而是直接调用。

当然也可以在消息的obtain()中设置callback属性。

post方法和send方法的区别

post方法在内部会调用send方法,并且post方法发送的是带有处理方法的消息,而send方法发送的是不带有处理方法的消息。前者消息在自己携带的方法中处理,后者则只能通过handleMessage()处理

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 08-03

Android异步消息处理机制(2)源码解析的相关文章

Android异步处理框架AsyncTask源码解析

一.概述 在Android开发中,我们进行异步处理一般会采用两种方式: 1.Thread +Handler 通常我们在Thread里面发送消息,然后在Handler的handleMessage方法里面去处理对应的任务,因为Android是不允许UI线程去更新UI的,这个时候我们可以采取这种方式 2.AsyncTask AsyncTask是Android为我们封装的一个轻量级的异步处理框架,其实底层也是用了类似Thread+Handler的方式.对外提供了一些方法,我们实现这些方法就可以很方便的进

Android异步消息处理机制(4)AsyncTask源码解析

上一章我们学习了抽象类AsyncTask的基本使用(地址:http://blog.csdn.net/wangyongge85/article/details/47988569),下面我将以问答的方法分析AsyncTask源码内容,源码版本为:API22. 1. 为什么必须在UI线程实例化我们的AsyncTask,并且必须在主线程中调用execute(Params... params)? 在分析为什么在UI线程调用之前,我们先看一下实例化AsyncTask并调用execute(Params...

[学习总结]6、Android异步消息处理机制完全解析,带你从源码的角度彻底理解

开始进入正题,我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃.相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现崩溃了. 这种处理方式被称为异步消息处理线程,虽然我相信大家都会用,可是你知道它背后的原理是什么样的吗?今天我们就来一起

Android异步消息处理机制详解及源码分析

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 最近相对来说比较闲,加上养病,所以没事干就撸些自己之前的知识点为博客,方便自己也方便别人. 1 背景 之所以选择这个知识点来分析有以下几个原因: 逛GitHub时发现关注的isuss中有人不停的在讨论Android中的Looper , Handler , Me

Android消息处理机制(源码分析)

前言 虽然一直在做应用层开发,但是我们组是核心系统BSP,了解底层了解Android的运行机制还是很有必要的.就应用程序而言,Android系统中的Java应用程序和其他系统上相同,都是靠消息驱动来工作的,它们大致的工作原理如下: 1. 有一个消息队列,可以往这个消息队列中投递消息. 2. 有一个消息循环,不断从消息队列中取出消息,然后处理 . 为了更深入的理解Android的消息处理机制,这几天空闲时间,我结合<深入理解Android系统>看了Handler.Looper.Message这几

Android异步消息处理机制(3)asyncTask基本使用

本文翻译自android官方文档,结合自己测试,整理如下. 概述 AsyncTask抽象类,翻译过来就是异步任务,能够合理并方便的使用UI线程.该类可以实现将后台操作结果显示在UI线程中,而不需要我们自己实现子线程或者handler(当然它内部也是借助这两者实现的). 虽然AsyncTask可以提供后台运行并将结果显示在UI上,但是理想情况应该是后台操作最多只能是几秒钟,若要执行长时间的操作强烈建议使用java中的Executor,ThreadPoolExecutor,FutureTask等.

grunt源码解析:整体运行机制&amp;grunt-cli源码解析

前端的童鞋对grunt应该不陌生,前面也陆陆续续的写了几篇grunt入门的文章.本篇文章会更进一步,对grunt的源码进行分析.文章大体内容内容如下: grunt整体设计概览 grunt-cli源码分析 grunt-cli模块概览 grunt-cli源码分析 写在后面 grunt整体设计概览 grunt主要由三部分组成.其中,grunt-cli是本文的讲解重点 grunt-cli:命令行工具,调用本地安装的grunt来运行任务,全局安装. grunt:本地grunt,一般安装在项目根目录下.主要

Android FM模块学习之四源码解析(二)

上一章我们了解了FM主activity:FMRadio.java,若没查看的,请打开链接Android FM模块学习之四源码解析(一) 查看fmradio.java源码注释.接下来我们来看看FM重要的一个类:FMRadioService.java 由上一章我们已经知道,打开FM时,在OnStart函数中会bindToService来开启服务, public boolean bindToService(Context context, ServiceConnection callback) { L

Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Android中的Looper , Handler , Message有什么关系?本篇博客目的首先为大家从源码角度介绍3者关系,然后给出一个容易记忆的结论. 1. 概述 Handler . Looper .Message 这三者都与Android异步消息处理线程相关的概念.那么什么叫异步消息处理线程呢?异步