前言

Android提供了Handler来满足线程间的通信,开发中不管直接还是间接基本离不开Handler的使用,通常Handler被我们用来做子线程更新UI线程的工具,可以说只要有子线程与主线程通信的地方就会有Handler。
工欲善其事必先利其器,熟悉Handler机制可以帮助我们更好的处理工作中遇到的问题。

1. 使用示例

private Handler mHandler;
public void test() {
    new Thread(){
        @Override
        public void run() {
            Looper.prepare();
            initHandler();
            Looper.loop();
        }
    }.start();
}
private void initHandler() {
    mHandler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
             // 处理消息
        }
    };
}

// 通过Handler发送消息
mHandler.sendMessage(message);
mHandler.post(runnable);
...

2. Java源码分析

主要涉及到以下几个类:

  • Message (消息实体)
  • Handler (处理Message)
  • MessageQueue (维护Message的队列,插入和取出Message)
  • looper (循环从MessageQueue中取Message)

Handler机制的核心是发送与处理Message,那么我们先过一遍Message类。

2.1 Message实体类

public final class Message implements Parcelable {
    ...

    // 处理Message的时间点
    public long when;
    // 持有处理Message的Handler对象
    /*package*/ Handler target;
    // 用来记录Message链表的下一个节点
    /*package*/ Message next;
    // Message复用池(也是链表格式)
    private static Message sPool;
    // Message复用池中当前Message个数
    private static int sPoolSize = 0;
    // Message复用池最大Message个数
    private static final int MAX_POOL_SIZE = 50;
    ...

    // 从Message复用池中获取Message,如果没有缓存才创建新的Message
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // 清除in-use标志
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
    void recycleUnchecked() {
        // 将消息标记为in-use,清空其他信息,插入Message复用池
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
    // 是否异步消息
    public boolean isAsynchronous() {
        return (flags & FLAG_ASYNCHRONOUS) != 0;
    }
    // 设置为同步或者异步消息
    public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
    }
    ...

}

Message是一个封装消息的实体类,主要对简单的消息做一层包装,方便后续处理,里面几个比较重要的字段和方法如上,其中when是待处理消息的时间点,target是Handler对象的引用,sPool是消息复用池,因为Android交互是基于消息机制,如果不进行复用,频繁的处理消息就会不断的创建销毁Message对象,导致内存抖动,所以创建Message时,Google推荐使用Message.obtain()来获取Message对象。recycleUnchecked()是将当前Message清空并插入复用队列。

2.2 发送Message流程

主要是通过Handler将Message按照时间When字段插入MessageQueue中。

2.2.1 Handler类

我们先来看sendMessage(message)和post(runnable)。

public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}

public final boolean post(@NonNull Runnable r) {
    return  sendMessageDelayed(getPostMessage(r), 0);
}

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

public boolean sendMessageAtTime(@NonNull 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);
}

可以看到,不管是sendMessage还是post都调用了sendMessageDelayed方法,而sendMessageDelayed里面调用了sendMessageAtTime并传入SystemClock.uptimeMillis() + delayMillis(当前时间+延迟时间)参数,也就是消息真正待处理的时间点,sendMessageAtTime又调用了enqueueMessage方法,通过方法名我们基本上就可以判断该方法是将消息插入队列,我们接着往下看。

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();

    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

在Handler的enqueueMessage方法中,调用了MessageQueue的enqueueMessage方法;注意这里有一个重点,Message的target指向了当前Handler对象,也就是说每个Message消息都会持有Handler对象的引用,这也就是为什么非静态内部类Handler可能会出现内存泄漏的原因。

2.2.2 MessageQueue类

将Message插入MessageQueue。

boolean enqueueMessage(Message msg, long when) {
    ...

    // 同步锁,多线程同步的核心
    synchronized (this) {
        ...

        // when = 当前时间 + 延迟时间
        msg.when = when;
        // 获取MessageQueue中保存的Message队列头
        Message p = mMessages;
        boolean needWake;
        // 如果Message队列为空或者新Message的when小于队列头
        // 将新Message插入到队列头部
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            // 如果阻塞则唤醒事件
            needWake = mBlocked;
        } else {
            // 插入到队列中间。通常不必唤醒事件队列,除非
            // 队列的头部有屏障,并且消息是队列中最早的异步消息。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            // 根据当前Message的when循环查找合适的位置插入,保证队列中
            // 的Message根据时间顺序排列。
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; 
            prev.next = msg;
        }
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

MessageQueue中持有一个链表形式的Message队列,enqueueMessage方法主要就是将待发送的Message根据时间顺序插入到消息队列中,并判断是否需要唤醒处理消息队列的线程,如果需要,通过调用native方法nativeWake()将该线程唤醒。

2.3 取出Message流程

主要是通过Looper循环从MessageQueue中拿出时间When字段与当前时间匹配的Message。

2.3.1 Looper类

Looper.prepare()主要是初始化Looper,并通过ThreadLocal能力保证此线程只有一个Looper,这里我就不细讲了。
我们看另外比较重要的Looper.loop()方法。

public static void loop() {
    ...
    for (;;) {
        Message msg = queue.next(); // 可能阻塞
        if (msg == null) {
            // 没有消息表明消息队列调用了退出
            return;
        }
        ...
        msg.target.dispatchMessage(msg);
        ...
        msg.recycleUnchecked();
    }
}

loop方法的主要作用是循环从MessageQueue中获取Message,并通过Message.Target(也就是Handler)调用dispatchMessage。

我们看看MessageQueue的next方法。

2.3.2 MessageQueue类

Message next() {
    ...

    int nextPollTimeoutMillis = 0;
    for (;;) {
        ...

        nativePollOnce(ptr, nextPollTimeoutMillis);
        // 同步锁,保证多线程之间Message数据同步
        synchronized (this) {
            // 尝试检索下一条Message,找到就返回
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // 同步屏障,查找下一条异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // 还未到消息的执行时间,计算时间差
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // 当前Message执行时间已到,返回此Message
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    msg.markInUse();
                    return msg;
                }
            } else {
                // 没有更多Message
                nextPollTimeoutMillis = -1;
            }
            ...

        }
        ...

        nextPollTimeoutMillis = 0;
    }
}

外部循环去检索Message,内部do while循环增加同步屏障检索异步Message,如果有异步Message则先处理异步,拿到Message之后判断时间when是否晚于当前时间,晚于当前时间的话计算时间差并保存到nextPollTimeMills,待下次循环开始然后调用native方法nativePollOnce()来进行阻塞,nativePollOnce()方法在native层使用到了Linux的epoll机制,并且native层也有一套Handler机制,这里先不做细致讲解,免得增加复杂度,总之内部在没有到处理消息的时间时会阻塞线程,释放CPU,直到拿到当前时间到达目标时间When的Message。

2.3.3 Handler类

接下来我们进入dispatchMessage看看做了啥。

public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

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

可以看到,dispatchMessage中判断msg.callback不为空则调用callback的run方法,也就是我们通过Handler.post(runnable)等传入的Runnable的run方法,否则调用handleMessage。

3. 整体流程

javaeventhandler机制 java handler机制的原理_android

4. 小结

  • Handler类主要处理收发消息,通过调用MessageQueue的enqueueMessage()方法将Handler发送的Message插入MessageQueue,并且内部有同步锁,保证多线程同步。
  • Looper主要通过loop()方法循从MessageQueue中取出Message,从MessageQueue中取出消息内部也有同步锁,保证多线程同步。
  • Handler消息机制,在Java层的MessageQueue中,调用了native方法实现线程的休眠和唤醒。native方法nativePollOnce()实现了消息循环的休眠,而nativeWake()方法则实现了消息循环的唤醒。