从用法解析源码:

Handler重要性:

  • 作为一个Android开发人员,只要一入Android这扇门,Handler就是一个伴随你开发终生的API。它的重要性不言而喻。但是我们虽然平时一直在用,却并没有真的研究过它的原理。
  • 今天我们来剖析剖析,写一篇你能懂的解析。

用法:

  • 万变不离其宗,我们首先还是聊一聊Handler的用法:
private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

mHandler.sendEmptyMessageDelayed(what,time);
  • 上面这段代码,使我们最常使用Handler的方式了。通过Handler对象发送消息,能够通知UI线程去做一些我们希望达成的事情。这是我们使用Handler最常用的地方(更新UI)。

从Handler构造器入手:

  • Handler的构造有多个重载,我们看下代码:
public Handler() {
    this(null, false);
}

public Handler(Callback callback, boolean async) {
    ...
    mLooper = Looper.myLooper();  //构建Looper
    if (mLooper == null) {
        throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;  //从Looper中取出消息队列
    mCallback = callback;     //设置Callback接口
    ...
}
  • 从上面,我们引出了好几个概念:
    • Looper(消息循环器)
    • MessageQueue(消息队列)
    • Message(消息)
    • Callback(回调)
  • 接下来我们来一个一个介绍他们

#Message

  • Message对象就很简单了,我们总说Handler用来发消息,发送的就是Message对象。
  • 它的开发中常用的what、obj等成员,我们就不细说了。不知道的同学,赶紧面壁思过去。
  • 需要介绍的,就是Message对象的target、sPool:

##target

  • target成员,其实就是Handler对象。
  • 每一个Message对象,都保存一个Handler成员,这个taget,表示的就是这个Message到底从哪一个Handler来。
  • 我们在来回顾下这段代码:
Message msg = Message.obtain();
msg.what = 1;
mHandler.sendMessage(msg);
  • 为什么mHandler发送的消息,最终能够回调到Handler自身的handleMessage呢?
  • 第一个原因,就是因为,Message对象持有了Handler引用,它知道它来自哪一个Handler。
public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;//在Handler调用obtain获取Message对象的时候,Message对象的target就被赋值了。

    return m;
}
  • 如上,在我们调用Message对象的生成逻辑的时候,其实Handler对象就已经传递进来并且设置给了target进行保存。
  • 这样就很清晰了,Message自己管理自己应该交给谁处理。这个设计,不得不说真的很巧妙。
  • 当然有的同学说,Message的obtain()函数明明可以不传入Handler。稍安勿躁,先坐下,这位扁桃体同学,不要着急发炎,我们后面再说。

##Message的创建、回收

  • sPool看名字就能知道是一个对象池。
  • 但是sPool的声明又是Message自己。
  • Message的模型设计是,用static sPool变量,链式管理对象池。
  • 我们先看看Handler的obtain()函数:
public final Message obtainMessage()
{
    return Message.obtain(this);
}
  • Handler获取消息,其实调用的是Message的static函数obtain()
  • 我们看看Message的obtain()
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;//首先看看sPool是否是null,不是的话将自己暂存
                sPool = m.next;//然后把Message管理的next交给sPool,这样sPool就是队列里的下一个Message
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
  • 注意看注释的两行,消息池的原理也很简单。每一个Message对象都管理着自己的链表下一个节点

  • 因此,Message对象也是一个链表形式的对象池进行管理的。

  • 那么问题来了,Message对象最多是多少个呢?

    private static final int MAX_POOL_SIZE = 50;//50个
    
  • Message对象还有一个需要注意的回收函数

    msg.recycleUnchecked();

  • 我们来看一看Message的回收过程:

void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    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交给自己的next
            sPool = this;//把自己作为sPool,这样在obtain()调用的时候就可以再次被利用了
            sPoolSize++;
        }
    }
}

  • 从这两段代码,基本可以知道,Message对象池的设计,其实就等于sPool是队头,每次取数据都是优先取队头的数据。而当obtain的数据过多的时候,又能不断新建对象作为缓冲。
  • 当然我们针对于Message的对象复用,只需要知道原理就好。只要记得Message最重要的其实还是自己管理了自己的分发Handler对象,Handler的消息就能从出发找到源头了。

MessageQueue

  • 顾名思义,它就是消息队列了。在Handler的构造中,我们能看到它是从Looper中取出来的。
  • MessageQueue就是存放我们发送的消息的队列,Handler的消息循环器(Looper)就是从它身上不断地获取消息进行处理的。
  • MessageQueue在整个Handler的体系中,只有一个,换句话说,每个线程只有一个MessageQueue。
  • 消息队列,其实不外乎就两个操作,一个入队一个出队。我们先来看看消息入队的操作。

消息入队:

  • 我们在使用Handler的时候,调用sendMessage等一系列的API,其实就是在发送消息进入MessageQueue队列。
  • 不管消息使用什么方式发送,延迟也好,立刻发送也好,Handler的消息发送函数最终都会回调到以下函数:
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);//将消息添加到队列中
}
  • 从这个函数看,其实也就是获取Handler自己的MessageQueue成员,然后进行下一步操作。
  • 而且发消息的操作,核心还是在enqueueMessage()函数中,我们来看看这个函数:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;//设置msg的回调target。这个target就是Handler本身
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);//果不其然,走到了MessageQueue的入队函数
}
  • enqueueMessage有一个细节值得注意的。它在拿到Message对象的时候,把Handler的this指针,置给了Message对象的target成员。
  • 这样,即使Message在obtain()的时候没有传入Handler,那消息进队列的时候,总会把Handler塞给target。刚才扁桃体发炎的同学,你可以消肿了 :D 。
  • 记笔记,要考!!!

###进入MessageQueue

  • 前面的调用过程都还是在Handler里,这下才真的进入到MessageQueue中。
  • 再来看MessageQueue的入队函数:
boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        //如果消息没有target就报错,因为不知道将msg交给哪一个Handler处理
    	//所以重点还是Message自己管理自己的处理着(Handler)
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        //如果msg已经在使用,也报错。
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {
        if (mQuitting) {
            //线程已经死了,肯定不会再入队
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();//这时候,开始标记消息在使用中
        msg.when = when;//消息的确切处理时间
        Message p = mMessages;//当前入队过的最后一条消息,注意不是队尾消息,因为可能是插入到队列中任何位置的
        boolean needWake;//是否需要唤醒Looper,其实Looper不是永远死循环的,没消息的时候,也会阻塞,避免出现性能问题
        if (p == null || when == 0 || when < p.when) {
            //如果上一条消息为null,表示是新队列,msg作为队头
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;//是否要唤醒循环器
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            //msg不在队头,就在队列中找到合适的位置
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            //将msg插入到链表中合适的位置
            msg.next  = p; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            //如果需要唤醒,就调用native函数,唤醒阻塞的队列
            nativeWake(mPtr);
        }
    }
    return true;
}
  • 整体代码看下来,基本可以清晰的知道Handler发出的消息最终是怎么进入到MessageQueue队列中的。
  • 从上面还可以看到一个特殊的变量needWake,它最终决定了是否需要调用一个native函数nativeWake()
  • 这里我们先简单介绍一下,这是一个唤醒消息循环器的机制,我们一会儿介绍Looper同学的时候,再让他出来发言。

小结

  • 从Handler的sendMessage走到这里,消息从创建到进入MessageQueue,其实就是简单的为Message是哪一个Handler处理,以及队列的数据结构操作。
  • 原理上来说,这部分还是比较简单的。

接下来

  • 有了Message和MessageQueue,后续我们还要再看看Looper、Callback,具体看看消息是怎么被取出来,怎么被处理掉的。

 评论