Handler事件分发机制

Handler基础

Handler事件分发相关类

1.Message:消息对象,可通过Bundle存储数据及Runable存储业务逻辑。
   Message的属性:
      long when;
        Handler target;
        Runnable callback;
2.Handler:发送,分发及处理Message。
   Handler的属性:
        MessageQueue mQueue;
        Looper mLooper;
        Callback mCallback;
3.MessageQueue:消息队列,保存Handler发送的消息。
4.Looper:循环从MessageQueue中读取消息交给Handler分发。
   Looper的属性:
        MessageQueue mQueue;
        Thread mThread;
5.ThreadLocal:保证每一个Thread中只有一个Looper。

发送Message相关函数

1.post(Runnable r):发送一个存储了业务逻辑的Message
2.postAtTime(Runnable r, long uptimeMillis):可设置“精确”时间,在你设置的这个时间之前,不会被MessageQueue next()出来给Looper
3.postAtTime(Runnable r, Object token, long uptimeMillis)
4.postDelayed(Runnable r, long delayMillis):发送延迟消息,内部调用是这样的sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
5.postAtFrontOfQueue(Runnable r):发送一个排在队列之前的Message,也就是说不管发送这个Message之前MessageQueue中有多少Message,下一个被处理的就是现在发送的消息。
6.sendMessage(Message msg)
7.sendEmptyMessage(int what)
8.sendEmptyMessageDelayed(int what, long delayMillis)
9.sendEmptyMessageAtTime(int what, long uptimeMillis)
10.sendMessageDelayed(Message msg, long delayMillis)
11.sendMessageAtTime(Message msg, long uptimeMillis)
12.sendMessageAtFrontOfQueue(Message msg)

Handler消息机制分发及处理流程分析

建立Handler消息机制

在Looper源码注释中有这么一段告诉了我们在普通Thread中怎么去使用Looper来建立Handler消息机制,我加入了Callback回调,以便下面的处理流程分析

class LooperThread extends Thread {
    public Handler mHandler;
    public void run() {
        Looper.prepare();//准备Looper
                Callback callback = new Callback(){
                    public boolean handleMessage(Message msg){
                        return false;
                    }
                };
        mHandler = new Handler(callback) {//实例化Hanlder
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };
        Looper.loop();//开启循环读取消息
    }
}

发送Message

发送消息的函数有很多,这里以post()函数为例子

mHnalder.post(new Runable(){
    public void run(){
        Log.d("tag_log","Runable执行了");
    }
})

发送一个Runable消息就是这样的了,接下来看下post()干了些什么:

public final boolean post(Runnable r) {
  return sendMessageDelayed(getPostMessage(r), 0);
}
/**将Runable赋值给Message**/
private static Message getPostMessage(Runnable r) {
  Message m = Message.obtain();//从消息池中获取一个Message对象
  m.callback = r;
  return m;
}

public final boolean sendMessageDelayed(Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

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);
}
/**将Message压入MessageQueue消息队列**/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
  msg.target = this;//记录发消息的Handler,自己发的消息自己分发自己处理
  if (mAsynchronous) {
      msg.setAsynchronous(true);
  }
  return queue.enqueueMessage(msg, uptimeMillis);
}

post()做了3件事情
1. 用Message包装Runable
2. 绑定发送Message的Hanlder对象
3. 将消息压入MessageQueue
从源码可以看出,不管事用post还是send去发送消息,最后都是将一个Message压入到MessageQueue。到此,消息的发送就已经结束了。那么是谁来从MessageQueue中拿Message又交给谁去处理呢?

Message分发及处理

在建立消息机制的时候有这么一段代码

    Looper.loop();//开启循环读取消息

这里就是Message分发的源头了,来看下它到底干了些什么

public static void loop() {
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;
        ...
    /**死循环不断读取及分发Message**/
    for (;;) {
        Message msg = queue.next(); //从队列中读取消息
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
                ...
        /**将Message交给Handler分发**/
        msg.target.dispatchMessage(msg);
                ...
        /**消息处理结束,进行回收**/
        msg.recycleUnchecked();
    }
}

loop()就做了3件事情
1. 读取消息
2. 将Message给msg.target分发
3. 将处理完成的Message回收
消息分发就在这里msg.target.dispatchMessage(msg),在enqueueMessage()函数中我们知道msg.target就是发送这个Message的Hanlder对象,所以到最后Message的分发还是Handler自己来处理的,好吧那我们来看下它是怎么分发消息的?

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {/**Message的Runable不为null,则直接运行Runable对象**/
            handleCallback(msg);
    } else {/**Message的Runable为null**/
            if (mCallback != null) {/**若Handler的Callback不为null,则将消息交给Callback处理**/
                    if (mCallback.handleMessage(msg)) {
                            /**如果Runable处理该消息之后返回true,则结束该消息的分发**/
                            return;
                    }
            }
            /**如果Runable处理该消息之后返回false,则继续将消息传递给Handler的handleMessage()处理,结束该消息的分发**/
            handleMessage(msg);
    }
}

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

看到了吧,到最后Message还是Handler自己来处理的。印证了enqueueMessage()中的那句注释,自己发送的消息自己分发自己处理。

Handler设置时间问题

起因

在查看源码的过程中,我发现Handler中是使用SystemClock.uptimeMillis()函数来获取时间,而平常我设置时间间隔一般都使用System.currentTimeMillis(),问题来了:
1. 那这两种方法有何区别呢?
2. 如果在postAtTime(Runnable r, long uptimeMillis)的时候使用System.currentTimeMillis()会怎么样?

区别

1. SystemClock.uptimeMillis()是从开机到现在的毫秒数(手机睡眠的时间不包括在内);
2. System.currentTimeMillis()是从1970年1月1日 UTC到手机当前时间的毫秒数;

但是,第2个时间,是可以通过System.setCurrentTimeMillis修改的,那么,在某些情况下,一但被修改,时间间隔就不准了。

能使用?

测试一

Handler handler = new Handler();
handler.postAtTime(new Runnable() {
    @Override
    public void run() {
        Log.d("tag_log", "Runnable执行了");
    }
},SystemClock.uptimeMillis());

Log正常打印出来了。

测试二

Handler handler = new Handler();
handler.postAtTime(new Runnable() {
    @Override
    public void run() {
        Log.d("tag_log", "Runnable执行了");
    }
},System.currentTimeMillis());

等了很久Log就是打印不出来了。这是为什么呢?

解决问题

最后跟踪源码发现传入的参数uptimeMillis赋值给了Message的when属性,而在MessageQueue的next()函数中有这么一段:

Message next() {
...
for (;;) {
    ...
    final long now = SystemClock.uptimeMillis();
    Message prevMsg = null;
    Message msg = mMessages;
    ...
    if (msg != null) {
            //msg.when就是我们设置的时间
        if (now < msg.when) {
            ...
        } else {
            ...
            msg.markInUse();
            return msg;
        }
    } else {
        ...
    }
}
}

也就是说,你给Message设置的时间比现在的SystemClock.uptimeMillis()时间大的话,next()函数就不会将你返回给Looper分发处理。随即我打印这两个时间做对比

tag_log: SystemClock.uptimeMillis():2546808
tag_log: System.currentTimeMillis():1461813573568

我靠System.currentTimeMillis()大太多了,所以说,如果我们在这里使用System.currentTimeMillis()的话,now < msg.when很长一段时间内都为true(在不手动System.setCurrentTimeMillis的话),这样我们发送的Message在手机不关机的几十年里也得不到执行了。我就是System.currentTimeMillis盲目使用者,幸运的是我以前只发送过延迟消息。。。。

你可能感兴趣的:(Handler)