A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 李印东老师 黑马帝   /  2014-7-29 22:56  /  2240 人查看  /  2 人回复  /   2 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 李印东老师 于 2015-3-27 09:15 编辑

Handler 消息机制(源码分析)
其中有四个关键对象分别是: Message,Handler, MessageQueue, Looper.

创建一个Message消息对象.
  1.    Message msg = new Message();
  2.    // 先去消息池中去取, 如果没有再new一个
  3.    Message msg = Message.obtain();
复制代码

创建一个Handler对象, 以下是Handler类的构造函数代码片段:
  1.    public Handler() {
  2.        ...         

  3.        // 取得Looper对象
  4.        mLooper = Looper.myLooper();
  5.        if (mLooper == null) {
  6.            throw new RuntimeException(
  7.                 "Can't create handlerinside thread that has not called Looper.prepare()");
  8.        }
  9.        // 消息队列
  10.        mQueue = mLooper.mQueue;
  11.        mCallback = null;
  12.     }
复制代码

调用sendMessage方法发送一个消息.
  1.    public final boolean sendMessage(Message msg) {
  2.        // 1. 转调sendMessageDelayed
  3.        return sendMessageDelayed(msg, 0);
  4.     }

  5.    public final boolean sendMessageDelayed(Message msg, long delayMillis) {
  6.        if (delayMillis < 0) {
  7.            delayMillis = 0;
  8.        }
  9.        // 2. 转调sendMessageAtTime
  10.        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
  11.     }

  12.    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
  13.        boolean sent = false;
  14.        MessageQueue queue = mQueue;
  15.        if (queue != null) {
  16.            // 3. 这里把msg的target参数赋值为this(当前的Handler对象)
  17.            msg.target = this;
  18.            // 4. 把消息传递给消息队列MessageQueue.
  19.            sent = queue.enqueueMessage(msg, uptimeMillis);
  20.        } else {
  21.            RuntimeException e = new RuntimeException(
  22.                 this + "sendMessageAtTime() called with no mQueue");
  23.            Log.w("Looper", e.getMessage(), e);
  24.        }
  25.        return sent;
  26.     }
复制代码

MessageQueue类, 通过enqueueMessage方法接收Message消息对象, 并按照先后或者时间顺序排列好.
  1.    final boolean enqueueMessage(Message msg, long when) {
  2.        ...         

  3.        final boolean needWake;
  4.        synchronized (this) {
  5.            ...

  6.            msg.when = when;
  7.            //Log.d("MessageQueue", "Enqueing: " + msg);
  8.            Message p = mMessages;
  9.            if (p == null || when == 0 || when < p.when) {
  10.                 msg.next = p;
  11.                 mMessages = msg;
  12.                 needWake = mBlocked; // newhead, might need to wake up
  13.            } else {
  14.                 Message prev = null;
  15.                 while (p != null &&p.when <= when) {
  16.                     prev = p;
  17.                     p = p.next;
  18.                 }
  19.                 msg.next = prev.next;
  20.                 prev.next = msg;
  21.                 needWake = false; // stillwaiting on head, no need to wake up
  22.            }
  23.        }

  24.        ...

  25.        return true;
  26.     }
复制代码

接下来就是Looper轮循器了, 它的工作就是不停的去MessageQueue队列中取消息. Looper的实例对象是在ActivityThread的main方法中初始化的. ActivityThread的main函数, 代码片段如下:
  1.    public static final void main(String[] args) {
  2.        ...

  3.        // 准备主线程的Looper轮循器, 内部会创建一个Looper对象.
  4.        Looper.prepareMainLooper();
  5.        if (sMainThreadHandler == null) {
  6.            sMainThreadHandler = new Handler();
  7.        }

  8.        ActivityThread thread = new ActivityThread();
  9.        thread.attach(false);

  10.        if (false) {
  11.            Looper.myLooper().setMessageLogging(new
  12.                    LogPrinter(Log.DEBUG,"ActivityThread"));
  13.        }

  14.        // 开始轮循.
  15.        Looper.loop();

  16.        ...
  17.     }
复制代码

Looper类中的loop方法, 代码片段如下; 注意: 这里loop方法是一个while(true)的死循环, 这就引发一个问题: 会不会阻塞主线程? 答案是: Looper的底层涉及到Linux下的管道(Pipe), 管道可以实现进程间通信, 管道内部实现原理: 是由一个特殊的文件来实现的, 此文件的两端有两个描述符, 一个用于读取, 一个用于写入. 这样的话主线程在等待其他线程写入, 如果其他线程写入后会唤醒主线程, 这样的话主线程开始循环取消息, 如果没有消息了, 主线程又开始进入等待(挂起)状态.
  1.    public static final void loop() {

  2.        // 获得主线程Looper对象
  3.        Looper me = myLooper();
  4.        // 获得消息队列
  5.        MessageQueue queue = me.mQueue;
  6.        while (true) {  // 循环的去消息队列MessageQueue中取消息.
  7.            // 注意: 如果消息队列中没有消息, 下面这一步就会使主线程进入挂起状态, 直到被唤醒, 再继续取消息, 分发消息.
  8.            Message msg = queue.next(); // might block
  9.            if (msg != null) {
  10.                 if (msg.target == null) {
  11.                     // No target is a magicidentifier for the quit message.
  12.                     return;
  13.                 }
  14.                 if (me.mLogging!= null)me.mLogging.println(
  15.                        ">>>>> Dispatching to " + msg.target + ""
  16.                         + msg.callback +": " + msg.what
  17.                         );

  18.                 // 调用target的dispatchMessage方法分发消息
  19.                msg.target.dispatchMessage(msg);
  20.                 if (me.mLogging!= null)me.mLogging.println(
  21.                        "<<<<< Finished to   " + msg.target + " "
  22.                         + msg.callback);
  23.                 msg.recycle();
  24.            }
  25.        }
  26.     }
复制代码

Looper调用了Message中的target的dispatchMessage方法分发消息, 上面Handler发送消息时,msg.target对象其实就是Handler自己.Handler的dispatchMessage方法如下:
  1. public void dispatchMessage(Message msg) {
  2.    if (msg.callback != null) {
  3.        handleCallback(msg);
  4.     }else {
  5.        if (mCallback != null) {
  6.            if (mCallback.handleMessage(msg)) {
  7.                 return;
  8.             }
  9.        }
  10.        // 调用了自己的handleMessage方法.
  11.        handleMessage(msg);
  12.     }
  13. }

  14. // 此方法是空实现. 是由我们自己定义的Handler对象中实现的.
  15. public void handleMessage(Message msg) {
  16. }
复制代码

至此为止, 发送一个消息最终被Handler的handleMessage方法接收到.





2 个回复

正序浏览
先收藏了再说,顶 !!!!!
回复 使用道具 举报
{:3_67:}!收藏了~  等需要的时候慢慢研究!
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马