Android Handler机制 源码解析

0. 前言

HandlerAndroid 开发过程可以说是必不可少的一枚棋,它保证了系统运行过程中的消息有序进行传递和处理。此文将在 Android 6.0 源码层面对 Handler 的运行机制进行简要剖析。

1. 总览

Handler的内部实现主要涉及到这三个类: Thread、MessageQueue和Looper。它们之间的关系可以用如下的图来简单说明:

关系结构

Thread是最基础的,Looper和MessageQueue都构建在Thread之上,Handler又构建在Looper和MessageQueue之上,我们通过Handler间接地与下面这几个相对底层一点的类打交道。

2. 从投递消息入手

2.1 最小突破口

首先创建Handler对象,来看构造方法:

// 默认构造方法
public Handler() {
        // 调用双参构造方法
        // 参数为 无回调 同步
        this(null, false);
    }

// 回调接口
public interface Callback {
    public boolean handleMessage(Message msg);
}

// 回调参数 是否异步参数 构造方法
public Handler(Callback callback, boolean async) {
    // FIND_POTENTIAL_LEAKS 为常量 false
    // if内代码 可能为预留接口代码
    if (FIND_POTENTIAL_LEAKS) {
        // 恒为false 不会执行
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
        }
    }
    // 取得当前线程中准备好了的Looper 
    // 下文会介绍
    mLooper = Looper.myLooper();
    // 如果为空 说明没有调用 Looper.prepare()
    if (mLooper == null) {
        throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
    }
    // 取得Looper中的消息队列 便于投递消息的入队操作
    mQueue = mLooper.mQueue;
    // 参数赋值
    mCallback = callback;
    mAsynchronous = async;
}

然后是投递消息:

// 发送即时消息
public final boolean sendMessage(Message msg) {
    return sendMessageDelayed(msg, 0);
}
// 发送延时消息
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

其实,经过观察,多个发送消息的方法其实最终都是调用的一个方法: sendMessageAtTime(...) ,如图所示:

函数间关系

2.2 准备投递

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);
}

2.3 包装消息

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 每个Message有一个指向Handler自身的Target
    // 用于后期对handleMessage()方法的回调
    msg.target = this;
    // 设定是否异步
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

关于异步
异步消息表示的是中断或者相对于同步消息而言不需要全局排序的事件。

2.4 入消息队列

接下来将目光移向 MessageQueue 的实现。

public final class MessageQueue {
    ...
    boolean enqueueMessage(Message msg, long when) {
        // 首先确保Handler存在
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        // 确保该消息是否被标记为已使用
        // 与msg.markInUse()方法呼应
        // 采用与运算判断,采用或运算置位
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }
        // 取得同步锁
        synchronized (this) {
            // 如果处于销毁状态
            // 由Looper控制
            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;
            // < mMessages是按照时间顺序排列的消息链表 >
            Message p = mMessages;
            boolean needWake;
            // 如果消息链表为空 或者时间无效
            // 或者当前即时消息将最先发生处理:
            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;
                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;
    }
    ...
}

至此,消息的入队列过程就完成了,也可以说是消息投递完成了,接下来就是邮递员 Looper 来取消息并且回调收件人 handleMessage() 方法了。

3. 从取消息回看

3.1 用法回顾

在寻找 Looper 的突破口时,我们先来回顾一下 LooperHandler 的配合用法:

// 一个新的线程
class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        // 首先调用Looper初始化操作
        Looper.prepare();
        // 创建Handler对象
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // 处理消息回调
            }
        };
        // Looper循环
        Looper.loop();
    }
}

可以看到,要使用 Handler ,必先调用 Looper 的相关方法,也就是说我们使用到安卓主线程中,必定在某一个地方悄悄地为我们调用了 Looper 的相关方法,使得我们可以直接使用 Handler 。那么接下来我们就去看看到底在哪里调用了?

3.2 主线程

在主线程 ActivityThread 中,可以找到Main函数,这也是一个应用启动的入口,在主函数中不难发现对 Looper 的相关方法调用:

public final class ActivityThread {
    ...
    public static void main(String[] args) {
        ...
        // 准备Looper
        Looper.prepareMainLooper();
        // 创建Handler
        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        // Looper循环
        Looper.loop();
        ...
    }
}

接下来就该对这几行代码分别展开作分析了。

3.3 Looper准备工作

准备分为两种情况,一种是非主线程,一种是主线程。
非主线程:

public static void prepare() {
    // 直接调用prepare方法 参数为允许销毁
    prepare(true);
}

主线程:

public static void prepareMainLooper() {
    // 调用prepare方法 参数为不允许销毁
    // 见下面方法
    prepare(false);
    synchronized (Looper.class) {
        // 如果sMainLooper不为空
        // 说明已经初始化 抛出异常
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        // 获取Looper并赋值
        sMainLooper = myLooper();
    }
}

// 存放Looper容器
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

// 准备函数 参数为是否运行过程中销毁
private static void prepare(boolean quitAllowed) {
    // 如果容器中已经有Looper
    // 说明已经创建 抛出异常
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    // 创建Looper并存入容器
    sThreadLocal.set(new Looper(quitAllowed));
}

// 返回容器中的Looper
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

3.4 Looper循环

Looper 准备好了,就要开始循环取出消息了。

// 循环方法
public static void loop() {
    // 先获取准备好的Looper对象
    final Looper me = myLooper();
    // 很明显不可以为空
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    // 取得Looper中的消息队列
    // 这和Handler中取得当前线程Looper中的消息队列是同一个
    final MessageQueue queue = me.mQueue;
    // 确保IPC身份为本地进程 同时记录身份
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    // 死循环
    for (;;) {
        // 从消息队列中取出消息
        // 时间未到时会阻塞
        Message msg = queue.next(); 
        if (msg == null) {
            // 如果为空 表示处于销毁状态
            return;
        }
        // mLogging是外部可对Looper设置Printer以输出Log
        Printer logging = me.mLogging;
        // 打印Log日志
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        // target为目标Handler 将Message分发过去
        // 下文细解
        msg.target.dispatchMessage(msg);
        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }
        // 打印日志
        final long newIdent = Binder.clearCallingIdentity();
        // 如果身份与之前的不同
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }
        // 回收消息
        msg.recycleUnchecked();
    }
}

这里读者可能会有疑惑,既然loop()方法中包含死循环,那程序执行到调用loop()不就卡住了,不会执行后续语句啊?对,没错,所以Looper.loop()方法,是ActivityThread主方法中的最后一条语句啊:

public final class ActivityThread {
    ...
    public static void main(String[] args) {
        ...
        Looper.loop();
        // 如果再执行到下面 说明循环意外终止了
        throw new RuntimeException("Main thread loop unexpectedly exited");
    }
}

最后,再对消息的分发作分析:

public class Handler {
    ...
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            // 触发Runnable回调
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                // 否则 触发Callback回调
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            // 再否则 直接回调handleMessage()方法
            handleMessage(msg);
        }
    }
}

这样的话,当消息触发后就发生回调,回调完成后消息被释放,一次消息传递之旅就圆满完成了。

总结

最后的总结,我想用一张图片来表达我对 Handler 机制的理解, THread 中包含多个 Handler 和一个 LooperLooper 中有一个消息队列,里面保存着需要触发的消息,每个消息包含了投递该消息的 Handler 对象, Looper 中的 loop() 方法循环取出消息,并回调 Handler ,这就是 Handler 机制的工作流程。希望笔者的文章对大家有所帮助,也祝大家学习进步!
总结

参考

  1. 深入源码解析Android中的Handler,Message,MessageQueue,Looper
  2. 《Android开发艺术探索》 任玉刚
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值