【Android】消息机制原理

标签: android 消息 原理 | 发表时间:2015-05-12 09:46 | 作者:xiaohui_hubei
分享到:
出处:http://blog.csdn.net

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

作者:xiaohui_hubei 发表于2015/5/12 9:46:22 原文链接
阅读:125 评论:0 查看评论

相关 [android 消息 原理] 推荐:

【Android】消息机制原理

- - CSDN博客推荐文章
Android 消息机制涉及到的类主要有. 下面结合 Android API 22 的源码分析上面几个类的内部实现细节,以窥探其中的原理一二. Looper 是一个循环处理消息的类,Looper内部维护一个消息队列,循环的从消息队列中取出消息并处理,如果队列为空则等待新消息. Looper 必须关联到某个线程中,这样其才能获得操作系统的调度而执行消息循环处理,默认情况下,通过 Thread 类创建的线程是没有 Looper 的,如果需要该线程拥有消息循环的功能,需要像下面这样在 Runnable 接口实现方法 run() 中创建Looper.

Android中BaseAdapter原理

- - 移动开发 - ITeye博客
            Android ListView理解,BaseAdapter ListView是Android开发过程中较为常见的组件之一,它将数据以列表的形式展现出来. 一般而言,一个ListView由以下三个元素组 成:. 1.View,用于展示列表,通常是一个xml所指定的. 大家都知道Android的界面基本上是由xml文件负责完成的,所以ListView的界 面也理所应当的使用了xml定义.

最新消息:Android 4.0新增功能

- martin - Tech2IPO
谷歌和三星于北京时间10月19日上午10点在香港召开新闻发布会,发布了代号为“冰激凌三明治”的谷歌新一代Android 4.0系统以及三星GALAXY Nexus智能手机. 发布会刚刚结束,我们看看Android 4.0新增功能. 用户桌面上点图表马上就可以进入浏览器界面,每一个浏览器标签上都有一个预览页,是多任务运行的.

android平台消息推送机制

- - ITeye博客
方案1、使用GCM服务(Google Cloud Messaging). 简介:Google推出的云消息服务,即第二代的G2DM. 优点:Google提供的服务、原生、简单,无需实现和部署服务端. 缺点:Android版本限制(必须大于2.2版本),该服务在国内不够稳定、需要用户绑定Google帐号,受限于Google.

Nexus Two配置及Android 2.3特性新消息

- Jokin - cnBeta.COM
本文包含有业内人士的猜测以及传闻,如果不想知道关于Google第二款自有品牌手机Nexus Two以及Android 2.3的传闻兴致消息,可以选择不看然后等到11月8日Google和三星的联合发布会查看官方消息. 首先是三星的“Nexus Two”手机(也有可能叫Nexus S)相关配置:.

iOS及Android消息推送方案安装使用入门

- - 乐无线-无线互联网观察
iOS消息推送直接使用苹果消息推送协议,服务器端采用easy apns: http://www.easyapns.com/. Android消息推送采用MQTT协议,服务器端采用mosquito+PhpMQTTClient  . 1 、      iOS 消息推送. 1.0 、消息推送证书生成.

使用ActiveMQ+MQTT实现Android点对点消息通知-转载

- - 开源软件 - ITeye博客
ActiveMQ使用MQTT协议,加上android上的paho包,即可简单实现消息通知功能,但是mqtt协议只支持topic,而且不能用selector,使得点对点的消息投递变成问题. 1、每个clientId,建一个topic...这个办法对解决消息点对点投递非常有效,但是有两个大问题:. 随着用户数增多,topic数量增多,对管理性要求增大,对内存的管理也有问题.

android瀑布流简单实现原理

- - ITeye博客
网上关于android瀑布流的例子一大堆,但是很多都是很复杂,对于新手来说有一定的难度. 原理很简单,就是异步下载图片,把图片addView到ScrollView(因为可以上下一直拖动)中,你需要屏幕显示几列就在ScrollView中放置几个LinearLayout,. 下面我就一个简单的例子来讲解android瀑布流的用法,样子很丑就不上图了.

Android 的提权 (root) 原理是什么?

- - 知乎每日精选
Android的内核就是Linux,所以Android获取root其实和Linux获取root权限是一回事儿. 你想在Linux下获取root权限的时候就是执行sudo或者su,接下来系统会提示你输入root用户的密码,密码正确就获得root权限了. Android本身就不想让你获得Root权限,大部分手机出厂的时候根本就没有su这个程序.

Android 进程回收之LowMemoryKiller原理篇

- - CSDN博客移动开发推荐文章
在前面的文章 Android进程保活一文中,对于LowMemoryKiller的概念做了简单的提及. LowMemoryKiller简称低内存杀死机制. 在讲解LowMemoryKiller之前,先看另一个概念:OOMKiller. Linux下有一种OOM KILLER 的机制,它会在系统内存耗尽的情况下,启用自己算法有选择性的kill 掉一些进程.