【Android】消息机制原理

Android 消息机制涉及到的类主要有

  • Looper
  • Handler
  • Message、MessageQueue
  • HandlerThread

下面结合 Android API 22 的源码分析上面几个类的内部实现细节,以窥探其中的原理一二。

Looper 消息循环

Looper 是一个循环处理消息的类,Looper内部维护一个消息队列,循环的从消息队列中取出消息并处理,如果队列为空则等待新消息。Looper 必须关联到某个线程中,这样其才能获得操作系统的调度而执行消息循环处理,默认情况下,通过 Thread 类创建的线程是没有 Looper 的,如果需要该线程拥有消息循环的功能,需要像下面这样在 Runnable 接口实现方法 run() 中创建Looper。

  class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();            // 创建与当前线程关联的 Looper 对象

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // 处理接收的消息
              }
          };

          Looper.loop();                // 开始消息循环
      }
  }

Looper.loop() 调用后,Looper 循环处理发送到其消息队列中的消息。后面我们会发现,消息处理的最终结果就是调用 Handler.handleMessage() 方法。

Looper 类

先看下 Looper 类的大致内容,为了突出重点,不重要的代码我省略了,只展示 Looper 类的重要成员变量和构造函数。源码如下:

public final class Looper {

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class

    final MessageQueue mQueue;        // 消息队列
    final Thread mThread;             // 关联的线程

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

从上面可以看到,Looper 类在创建时,会创建一个消息队列,并关联到当前线程。

Looper.prepare()

Looper 对象的构造函数是private 修饰,不能直接通过 new 来创建,只能通过其静态方法 Looper.prepare() 来创建,同时注意到”sThreadLocal.get() != null“这句代码判断是否已存在 Looper 对象,如果 Looper 对象已经存在则会抛出异常,所以每个线程最多只能有一个 Looper 对象。源码如下:

    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.loop()

一旦调用 Looper.loop() 方法,Looper 对象开始消息循环,其过程很简单,首先检查消息队列是否为空,如果消息队列为空就退出,否则从消息队列中取出一个消息,分发给消息中指定的 Handler 对象处理,然后回收消息放入消息池,然后循环该过程。源码如下:

    public static void loop() {
        // 获取当前线程的 Looper 对象
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread.");
        }
        // 获取当前线程 Looper 对象内部的消息队列
        final MessageQueue queue = me.mQueue;

        ...

        for (;;) {
            Message msg = queue.next(); // 下一个消息
            if (msg == null) {
                // 如果没有消息表明消息队列将要退出
                return;
            }

            ...
            // 分发消息
            msg.target.dispatchMessage(msg);

            ...

            // 回收消息放到消息池中
            msg.recycleUnchecked();
        }
    }

Looper 类其他有用方法

  • static Looper myLooper()

    返回与当前线程关联的 Looper 对象。在任意线程调用Looper.myLooper()返回的都是那个线程的looper对象。源码如下:

    public static Looper myLooper() {
        return sThreadLocal.get();
    }
  • static MessageQueue myQueue()

    返回与当前线程关联的消息队列。源码如下:

    public static MessageQueue myQueue() {
        return myLooper().mQueue;
    }

总结

1.每个线程有且最多只能有一个Looper对象,它是一个ThreadLocal

2.Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行

3.Looper使一个线程变成Looper线程。

Handler

Handler 不仅用于处理消息,还用于发送消息(到线程所关联的消息队列)。

Handler 对象发送消息到消息队列后(sendMessage),Looper 进行消息循环处理(loop),将消息传递(dispatchMessage)给该消息创建时所指定的 Handler 对象进行处理(handleMessage)。

这里需要理清几个数量对应关系:

* 一个 Thread 仅与一个 Looper 关联

* 一个 Looper 只包含一个 MessageQueue

* 一个 Handler 仅与一个 Looper 关联 (这样每个Handler知道将消息发送给哪个Looper)

* 一个 Message 仅与一个 Handler 关联 (这样每个Message知道将由哪个Handler处理)

  • 多个 Handler 可以关联同一个 Looper(即关联不同Handler的消息可以放在同一个消息队列中)

Handler 类

Handler 类的成员变量和构造函数如下:

public class Handler {
    final MessageQueue mQueue;        // 关联的消息队列
    final Looper mLooper;             // 关联的 Looper
    final Callback mCallback;         // 回调接口
    final boolean mAsynchronous;      // 是否异步

    // 最常见的一种方式,关联当前线程的Looper和消息队列
    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) {
        ...

        mLooper = Looper.myLooper();    // 关联到当前线程的 Looper 对象
        if (mLooper == null) {          // Looper不能为空,Handler只能在关联了Looper的线程中创建
            throw new RuntimeException(
                "Can‘t create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;    // 关联消息队列,Handler发送的消息就存放在该消息队列中
        mCallback = callback;
        mAsynchronous = async;
    }

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

    ...
}

从 Handler 的构造函数可以知道,平时我们写的 “mHandler = new Handler(){…}” 这句代码,内部其实已经关联了当前线程的消息循环和消息队列。所以我们创建 Handler 后不需要做这些关联工作,只需要重写 handleMessage() 方法添加自己的业务逻辑,这种设计方式让使用者编写更少的代码。

Handler 发送消息

handler 发送消息有两种方式:

  • send message

    通过sendMessage、sendMessageDelayed、sendMessageAtTime等方法,发送 Message 对象。

  • post Runnable Object

    通过 post、postDelayed、postAtTime等方法,发送实现了 Runnable 接口的对象。这种方式其本质也是发送消息,内部通过将 Runnable 对象封装成 Message 然后发送。

结合源码看可能更清楚些,所有发送消息的方法都是通过enqueueMessage(...)方法实现的,发送Runnable对象时,先通过getPostMessage(...)方法将Runnable 对象封装成 Message 然后通过发送消息的函数发送。

    // 将消息 msg 添加到消息队列 queue 中
    // uptimeMillis 表示系统从启动开始计时,正常运行 uptimeMillis 毫秒后分发出去(单位:毫秒)
    // uptimeMillis 值越小消息越早得到处理。
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

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

    // 延迟 delayMillis 指定毫秒时间后发送消息
    // SystemClock.uptimeMillis() 可获取系统从启动到现在正常运行的时间,不包含睡眠时间(单位:毫秒)
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

    /* post 方式 */

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

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

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    // 其他几个 send 和 post 方法的实现类似(通过调用其它成员函数实现),这里不一一举出
    ...

Handler 处理消息

前面在将 Looper 时已经提到,Looper 对象调用 loop() 方法后开始循环处理消息队列,对消息的处理其实就是调用消息所绑定的 Handler 对象的 dispatchMessage 方法。

// 分发消息
msg.target.dispatchMessage(msg);

Handler类的 dispatchMessage 方法是处理消息的地方,其源代码如下:

public class Handler {
    ...

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            // 如果消息设置了callback对象(即Runnable接口),则执行callback
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                // 如果 Handler 自身设置了 callback 对象,则执行callback
                // 如果返回true,则返回,否则继续执行
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 调用Handler类的handleMessage方法处理消息
            handleMessage(msg);
        }
    }

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

    Callback mCallback;

     /* 这种方法允许让activity等来实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法。参考 http://alex-yang-xiansoftware-com.iteye.com/blog/850865  */
    public interface Callback {
        public boolean handleMessage(Message msg);
    }

    // Handler 的子类必须重写该方法实现对消息的处理
    public void handleMessage(Message msg) {
    }

    ...
}

如果发送消息采用的是 post Runnable 对象,那么 Message.callback 对象不为空。在处理消息时,处理 Message.callback 后退出,Handler 的成员函数 handlerMessage() 方法不会调用。所以如果采用 post Runnable 对象方式发送消息,我们不必重写 Handler 类的 handlerMessage() 方法。

传递 Handler

主线程是一个拥有handler和Looper的消息循环(默认创建好的)。主线程上创建的Handler会自动与它的Looper

相关联。我们可以将主线程上创建的Handler传递给另一线程。传递出去的Handler与创建它的线程 Looper 始终保持着联系。因此,任何已传出 Handler 负责处理的消息都将在主线程的消息队列中处理。

下图中,主线程和后台线程都关联了各自的 Looper,并有各自的 Handler,主线程需要下载图片时创建后台线程,同时将自己的 Handler 传递给后台线程,后台线程通过自己的 Handler(mHandler)发送下载图片的消息,并处理下载图片消息。如下图:

也可以反过来,通过后台线程想主线程发送消息。后台线程在自己的 Handler 中处理完下载图片消息后,通过主线程的 Handler(mResponseHandler)发送图片下载完毕的消息,然后主线程在自己 Handler 中处理这个消息(如将图片进行显示)。如下图:

Message

Message 可以看作是一个数据结构,用于保存消息相关的数据。MessageQueue内部使用了队列实现,并处理了并发访问,这里对其不做过多深入,只要知道 MessageQueue 是一个存储消息的队列即可。

创建 Message 实例

创建 Message 最好使用方法 Message.obtain() 或者 Handler.obtainMessage() 方法,因为其创建的 Message 实例来自消息池,可以减少创建新消息实例的时间和空间开销,该方法有很多重载形式。

public final class Message implements Parcelable {
    /* 下面字段是 public 访问权限,可以直接访问 */
    public int what;        // 用户定义的消息消息码用于区分不同消息
    public int arg1;        // 保存int数据
    public int arg2;        // 保存int数据
    public Object obj;      // 发送给接收者的任意对象
    ...

    // 从全局消息池中返回一个新的消息实例
    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();
    }

    // 返回一个新的消息实例,并通过传入的参数初始化返回的消息实例成员字段
    public static Message obtain(Handler h, int what,
            int arg1, int arg2, Object obj) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        m.arg1 = arg1;
        m.arg2 = arg2;
        m.obj = obj;

        return m;
    }

    // 其他静态的 obtain() 方法类似,通过参数初始化返回的消息实例的部分成员字段
    ...

}

消息回收池

消息内部用一个链表结构实现了消息池,同时还有一个记录消息池当前大小的静态变量。回收消息时将消息放入链表,同时大小增 1;通过 Message.ObtainMessage() 从消息池获取消息时,从消息链表中取出一个消息,同时大小减 1。

public final class Message implements Parcelable {
    ...

    /*package*/ Message next;
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;

    ...

    // 回收消息实例,即放入到全局的消息池中
    public void recycle() {
        if (isInUse()) {
            if (gCheckRecycle) {                     // 如果当前消息实例正在使用,并且开启了检查消息回收,则抛出异常
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
        recycleUnchecked();        // 执行未受检查的消息回收
    }

    /* 回收未受检查的消息,消息可能正在使用中 */
    void recycleUnchecked() {
        flags = FLAG_IN_USE;       // 标记当前消息实例为"正在使用"
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = -1;
        when = 0;
        target = null;
        callback = null;
        data = null;

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

    ...
}

Message 中保存 Bundle 类型数据

除了通过上面介绍的 Message 类的 public 成员字段保存数据外,Message 类还提供了 Bundle 类型的数据保存。

public final class Message implements Parcelable {
    ...

    Bundle data;

    // 返回当前消息中保存的 Bundle 对象,如果没有,则创建一个空的Bundle对象并返回
    public Bundle getData() {
        if (data == null) {
            data = new Bundle();
        }

        return data;
    }

    public void setData(Bundle data) {
        this.data = data;
    }

    // 返回当前消息中保存的 Bundle 对象,可能为空
    public Bundle peekData() {
        return data;
    }

    ...
}

Message 绑定 Handler

前面讲 Handler 时说过,每个 Message 只能与一个 Handler 关联,Message 对象中关联的 Handler 用于处理该消息。可以通过 Message 类的 sendToTarget() 方法发送消息,这与调用 Handler 类的 sendMessage() 本质一样。

public final class Message implements Parcelable {
    ...

    Handler target;        // 当前 Message 对象关联的 Handler 对象

    public void setTarget(Handler target) {
        this.target = target;
    }

    public Handler getTarget() {
        return target;
    }

    // 发送消息,实际就是调用 Handler 类的 sendMessage() 方法
    public void sendToTarget() {
        target.sendMessage(this);
    }
    ...
}

HandlerThread

Android 中的主线程(UI线程)创建时已经创建了一个 Looper,因此主线程中可以直接创建 Handler 并发送和处理消息。可以通过 Context 类中的成员方法 getMainLooper() 获取当前进程中主线程的 Looper。

// 在 Acitivity 中
this.getMainLooper();

// 在 Fragment 中
getActivity().getMainLooper();

但是通过 Thread 类创建的其他线程是没有 Looper 的,需要我们手动创建 Looper,为了省去手动创建 Looper,Android 提供了HandlerThred 这个便捷类,用于创建一个带 Looper 的线程。其实现很简单,下面是他的完整源码,由于不多我就全部贴出来了:

package android.os;

public class HandlerThread extends Thread {
    int mPriority;        // 线程优先级
    int mTid = -1;        // 线程 id
    Looper mLooper;       // 消息循环

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    /* 子类重写该方法,添加消息循环开始前的准备操作,例如创建 Handler */
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();    // 创建 Looper 对象
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);    // 设置当前线程优先级
        onLooperPrepared();    // 调用成员方法
        Looper.loop();         // 进入消息循环
        mTid = -1;
    }

    /* 获取当前线程的 Looper 对象 */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;    // 线程没有启动时,返回 null
        }

        // 如果线程已经被启动, 但 Looper 还没创建完成,则等待直到 Looper 创建 */
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

    /**
     * 退出线程的消息循环
     * Looper 退出后,任何发送给当前线程消息队列的消息都将失败,即方法返回 false
     * 使用这个方法是不安全的,因为在 Looper 退出前可能有些消息还没有分发出去
    */
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quit();
            return true;
        }
        return false;
    }

    /**
     * 安全地退出线程的消息循环
     * 该方法之所以安全是因为它会在退出前先将消息队列中剩余消息都分发出去,不过对于那些
     * 延迟消息依然不会分发出去,因为当前Looper需要尽快退出,不会为了不确定的延迟时间而
     * 等待。
     */
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
            looper.quitSafely();
            return true;
        }
        return false;
    }

    // 返回线程 Id
    public int getThreadId() {
        return mTid;
    }
}

HanderThread 类的使用,一般自定义一个继承自 HandlerThread 的类,然后重载其 onLooperPrepared() 方法。使用 HandlerThread 类时,需要先调用 start() 方法将线程启动起来,然后调用 getLooper() 方法以确保该线程的 looper 创建完成,之后可以通过关联该线程的 Handler 发送和处理消息了。

参考

[1] Android SDK Documents

[2]《Android 编程权威指南》

[3] android的消息处理机制(图+源码分析)——Looper,Handler,Message

时间: 2024-10-09 19:13:30

【Android】消息机制原理的相关文章

android消息机制原理详解

android消息机制原理详解 因为之前使用的是CSDN默认的文本编辑器,而且也因为懒得学用MarkDown来写博客,所以排版上有一些问题.就上一篇写的设计模式之抽象工厂模式提出了这个问题(一个android群的群友提出来的,没有在评论里评论),所以以后的文章都用MarkDown来写了. 好了,言归正传,这篇文章我来介绍一下android消息机制的原理 Android消息机制概述 说到Android的消息机制,Android初级工程师(不包括那些初学者)肯定会想到Handler.是的,Andro

Android消息机制Handler的实现原理解析

Android的主线程为什么可以一直存在? 线程是一个动态执行的过程,从产生到死亡包括五个状态:新建.就绪.运行.死亡和堵塞.只要线程没有执行完毕或者没有被其它线程杀死,线程就不会进入死亡状态.Android中的主线程一直存在是因为主线程中一直在监听消息,从而使线程无法被执行完毕. 线程的五种状态: 新建new Thread 当创建Thread类的一个实例对象时,此线程进入新建状态未被启动. 就绪runnable 线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等

Android6.0 消息机制原理研究

 消息都是存放在一个消息队列中去,而消息循环线程就是围绕这个消息队列进入一个无限循环的,直到线程退出.如果队列中有消息,消息循环线程就会把它取出来,并分发给相应的Handler进行处理:如果队列中没有消息,消息循环线程就会进入空闲等待状态,等待下一个消息的到来.在编写Android应用程序时,当程序执行的任务比较繁重时,为了不阻塞UI主线程而导致ANR的发生,我们通常的做法的创建一个子线程来完成特定的任务.在创建子线程时,有两种选择,一种通过创建Thread对象来创建一个无消息循环的子线程:

Android 消息机制 (Handler、Message、Looper)

综合:http://blog.csdn.net/dadoneo/article/details/7667726 与 http://android.tgbus.com/Android/androidnews/201204/421642.shtml 一. 消息机制常用类的介绍和使用 在Android程序运行中,线程之间或者线程内部进行信息交互时经常会使用到消息,如果我们熟悉这些基础的东西及其内部的原理,将会使我们的Android开发变的容易.可以更好地架构系统.在学习Android消息机制之前,我们

网易视频云技术分享:Android 消息机制学习

Android消息机制大家都不陌生,想必大家也都看过Handler.Looper的源码(看过可以直接看末尾重点,一款监控APP卡顿情况的控件),下面,网易视频云技术专家就整合一下这方面的资料,加深对这方面的印象. 用法 private Handler mHandler = new Handler() {    @Override public void handleMessage(Message msg) {        switch (msg.what) {            case

android Binder机制原理

Android Binder机制原理(史上最强理解,没有之一)(转) 原文地址: http://blog.csdn.net/universus/article/details/6211589 Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection): 管道(Pipe).信号(Signal)和跟踪(Trace).插口(Socket).报文队列(Message).共享内存(Share Memo

【转】Android 消息机制

Android 消息机制 本文首先从整体架构分析了Android整个线程间消息传递机制,然后从源码角度介绍了各个组件的作用和完成的任务.文中并未对基础概念进行介绍,关于threadLacal和垃圾回收等等机制请自行研究. 基础架构 首先,我们需要从整体架构上了解一下Android线程通信都做了哪些工作.我们都知道,进程是操作系统分配资源的最小单位,一个进程中可以启动多个线程来执行任务,这些线程可以共享进程的资源但不分配资源,这里讲的资源主要是只内存资源.Android的线程间消息传递机制其实和我

Android消息机制字典型探

Android消息机制字典型探究(一) Android消息机制字典型探究(二) 带着这篇去通关所有Handler的提问(三)

Android消息机制:Looper,MessageQueue,Message与handler

Android消息机制好多人都讲过,但是自己去翻源码的时候才能明白. 今天试着讲一下,因为目标是讲清楚整体逻辑,所以不追究细节. Message是消息机制的核心,所以从Message讲起. 1.Message是什么? 看一个从消息池中取出一个msg的方法: public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) { Message m = obtain(); m.target = h; m