阅读 700

作为一个帝国的管理者,你喜欢Handler吗?

Handler,帝国使命管理者

与每个Message所关联,每条Message都会持有一个Handler,同时负责消息的发送,以及消息的分发。接下来看看Handler源码:

//***************************Handler的构建***************************
//在创建Handler的时候,Looper、MessageQueue同时会出现
//在传入Looper的情况下
public Handler(Looper looper) {
    this(looper, null, false);
}
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
​
//在没有传入Looper情况下
public Handler() {
    this(null, false);
}
public Handler(Callback callback, boolean async) {
    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
​
//***************************Handler消息发送***************************
//最终是调用的这个私有方法
/**
 * @param uptimeMillis 经过计算所获的一个时间戳,对应每条消息
 */
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;//消息持有当前Handler
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //调用MessageQueue的入队方法,将消息入队
    return queue.enqueueMessage(msg, uptimeMillis);
}
​
//***************************Handler消息分发***************************
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        //我们一般发送的post消息会走这里,由Message的CallBack回调。也可以发现这里回调的优先级。
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            //这是构建Handler传入Callback
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //如果没有传入Callback,就从重写的handleMessage回调
        handleMessage(msg);
    }
}
复制代码

MessageQueue,帝国士兵队列

维护当前线程所有的Message,并会对进队的消息按照Message所携带的when时间戳进行排序。就像是按照身高在排前后顺序一样。Handler里的延时消息就与入队所携带的时间戳有关。

//出队
Message next() {
        //*******省略**********
        for (;;) {
            //*******省略**********
            //堵塞线程?
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                //获取一个时间戳
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                //*******省略**********
                if (msg != null) {
                    //判断当前消息是否需要延时
                    if (now < msg.when) {
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //返回当前消息
                        return msg;
                    }
                } else {
                    //队列为空
                    nextPollTimeoutMillis = -1;
                }
                //*******省略**********
            }
            //*******省略**********
        }
    }
复制代码
//Handler会调用这个方法将Message入队
/**
 * @param msg   Message
 * @param when  当前消息所携带的时间戳
 */
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) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else { 
                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;
    }
复制代码

Looper,帝国的将军

通过ThreadLocal 保证了同一线程中自己的唯一性,在自己私有的构造方法中,又保证了自己管理的士兵队列MessageQueue的唯一。当将军开始点兵的时候,源码就来了:

public static void loop() {
        //*******省略**********
        //当前Looper所关联的MessageQueue
        final MessageQueue queue = me.mQueue;
        //*******省略**********
        //循环查询消息
        for (;;) {
            //*******省略**********
            /**
             * 关键的一行,是使用Message所关联的Handler进行分发消息。
             * 每个线程有唯一的Looper、MessageQueue管理Message。但是Handler
             * 是没有限制的,并且消息和Handler是关联的。
             */
            msg.target.dispatchMessage(msg);
            //*******省略**********
        }
    }
复制代码
    //这是Handler中的方法
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            //我们一般发送的post消息会走这里,由Message的CallBack回调。也可以发现这里回调的优先级。
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                //这是构建Handler传入Callback
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //如果没有传入Callback,就从重写的handleMessage回调
            handleMessage(msg);
        }
    }
​
复制代码

Message,帝国的士兵,使命承载者

信息的携带者,有点像个Entity,哈哈~~,查看几个关键的位置:

//甄别消息的标志,士兵的腰牌
public int what;
//所传送的消息内容,士兵的使命内容
public Object obj;
//当前消息关联的时间戳。也是完成使命的时间。这个时间戳获取方式一定要注意
long when;
//当前消息所持有的Handler
Handler target;
//我们在使用postDelayed时候传入的回调,就是他了。
Runnable callback;
//MessageQueue的基础,也像极了士兵的队列
Message next;
复制代码

你们的皇帝回来了(串流程)

  1. 构建Handler(同时产生Looper、MessageQueue)

  2. 发送消息:调用sendMessage(),最终会调用MessageQueueenqueueMessage()方法入队,这时候会把入队消息进行排序,等待获取。

  3. Looper的loop启动时机有两个

    1. ActivityThread的main方法调用(系统调用)
    2. 手动进行调用
  4. 当Looper的循环启动,从MessageQueue中取出适当消息后,就会通过当前消息所关联的Handler进行分发dispatchMessage()

疑惑问答

  1. Handler的延时消息是如何实现的?

在每一个Message中,都会保存一个相对消息的时间戳(毫秒)when,在Looper循环取消息的时候会进行判断。

  1. 是一个线程维护一个Looper,一个Looper对应一个MessageQueue?

从源码可以发现,在调用Looper.prepare()时,如果在ThreadLocal中获取Looper不为null,就会抛出异常throw new RuntimeException("Only one Looper may be created per thread")。MessageQueue的初始化又在Looper的构造方法中,所以他们时一一对应的。

文章分类
Android
文章标签