Handler之工作原理源码解析

0 引言

Android消息机制的核心类为Handler、Looper、Message、MessageQueue

  • Handler:名为Handler,它的主要功能就是处理消息,同时它还具有创建消息(Message),发送消息等功能
  • Looper:内部维护一个消息队列(MessageQueue),同时提供了进入/退出消息循环的功能。同时内部还有一个静态ThreadLocal对象,为每个线程都维护了一个Looper对象

  • Message:消息主体,封装了待处理的消息数据

  • MessageQueue:消息队列,负责维护消息

1 工作原理

下面我们从一个最简单的例子来分析消息机制的工作原理

Thread{
    Looper.prepare()
    Handler().post {
        Toast.makeText(this@MainActivity, "I'm a Toast", Toast.LENGTH_SHORT).show()
    }
    Looper.loop()
}.start()

上述代码实现的功能很简单,其功能是在一个非主线程中显示一个Toast。主要分为三个步骤

  1. 调用Looper.prepare函数进行准备
  2. 创建Handler并调用其post函数发送消息
  3. 调用Looper.loop函数进入循环并开始处理消息

1.1 准备

Looper.prepare最终调用的是以下函数

private static void prepare(boolean quitAllowed) {//此时quitAllowed为true
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}

该函数主要是创建了一个Looper对象,并保存在了sThreadLocal中。

sThreadLocal是一个ThreadLocal对象,通过这种方式,后续就可以通过Looper.myLooper函数获得线程对应的Looper对象了。

再来看看Looper的构造函数

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

创建了一个MessageQueue实例,同时获得当前线程的Thread对象

MessageQueue的构造函数也十分简单,仅仅是使用quitAllowed初始化了mQuitAllowed字段,代码如下

MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed;
    //...
}

至此第一步的代码分析完毕

总结:第一步的主要工作是创建Looper以及MessageQueue

1.2 发送消息

第二步首先创建了一个Handler对象,由于内部调用了重载的构造函数,所以最终调用的代码如下

public Handler(Callback callback, boolean async) {//callback=null,async=false
    //...

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

Looper.myLooper的代码十分简单,只是返回了sThreadLocal.get(),也即是在第一步中创建的Looper对象。

可以看到,Handler持有了Looper、MessageQueue对象的引用。

Handler创建完毕,接下来就调用了post函数

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

该函数首先通过getPostMessage函数生成了一个Message对象,然后再调用了sendMessageDelayed

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}

该函数非常简单,通过Message.obtain函数获得一个Message对象,保存r至callback字段中,然后返回

getPostMessage函数经过若干函数调用最终会走到enqueueMessage函数中

private boolean enqueueMessage(MessageQueue queue,//Handler构造函数中保存的mQueue
                               Message msg,
                               long uptimeMillis) //uptimeMillis表示该消息应该被处理的时间点
{
    msg.target = this;
    if (mAsynchronous) {//消息机制中的异步机制,暂时先不管
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

函数首先保存当前Handler至Message的target字段中

然后调用了MessageQueue.enqueueMessage函数

//MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
    //一些前置检查

    synchronized (this) {
        //检查MessageQueue退出检查

        msg.markInUse();//将Message标记为使用中
        msg.when = when;
        Message p = mMessages;//MessageQueue中使用链式结构来组织Message,每个Message都有一个next字段来指向下一个Message
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            // MessageQueue中无消息
            // 或者when的值为0
            // 或者when的值小于链表头部Message对象的话when字段值
            // 那么将msg插入到链表的头部
            msg.next = p;
            mMessages = msg;
            //...
        } else {
            //...
            // 遍历链表,将msg插入到指定位置
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                //...
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }

        //...
    }
    return true;
}

enqueueMessage的功能简单,主要是负责将新的消息插入到消息链表中,插入时需要遵循when较小的Message排在前面,when较大的排在后面。

总结:第二步的核心功能就是通过Handler将Message插入到MessageQueue的消息链表中

1.3 进入消息循环

//Looper.java
public static void loop() {
    final Looper me = myLooper();
    //..
    final MessageQueue queue = me.mQueue;

    //...

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        //...

        msg.target.dispatchMessage(msg);
        //...
    }
}

loop函数的脉络也是十分清晰的

  1. 通过调用MessageQueue.next函数获得需要处理的Message
  2. 调用Message.target(Handler类型)的dispatchMessage函数来处理消息
1.3.1 获取待处理的消息
//MessageQueue.java
Message next() {
    //...

    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        //...

        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            //...
            if (msg != null) {
                // 对比当前时间与链表头部Message.when字段
                if (now < msg.when) {                    
                    // 如果下一个消息的处理时间还没到,那么设置一个等待超时时间
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    //region 拿到可用的msg,返回
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {//prevMsg!=0的情况先不考虑
                        prevMsg.next = msg.next;
                    } else {
                        //链表头部移动到链表第二个元素上
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    //返回链表中的第一个元素
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            //...
        }

        //...

    }
}

next函数的核心功能不复杂,首先判断当前时间与链表头部的Message.when字段的大小,如果该Message可以处理,那么直接返回链表头部的Message,否则继续等待。

1.3.2 处理消息

待处理的消息已经拿到了,那么接下来就是处理消息了

消息的处理还是通过Handler

代码中使用了msg.target.dispatchMessage来处理消息,Message中的target字段是在1.2小节的enqueueMessage函数中设置的,也即是发送该消息的Handler。

//Handler.java
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);//该函数只有一句代码:msg.callback.run();
    } else {
        //...
        handleMessage(msg);
    }
}

dispatchMessage逻辑十分简单,当Message的callback字段不为空,使用handleCallback函数进行处理,否则使用handleMessage函数进行处理。

对于callback的处理比较简单,handleCallback中直接调用其run函数就结束了

handleMessage则需要我们自己重写去实现自定义的处理方式。

总结:调用Looper.loop函数进入死循环,循环内部不停地获取消息、处理消息

总结

本文以一个简单的例子为切入点,结合源码分析了Handler工作的基本流程

  1. 初始化消息队列(Looper、MessageQueue)
  2. 创建Message,并通过Handler将Message加入到MessageQueue的消息链表中
  3. 调用Looper.loop函数是当前线程进入死循环不停地从MessageQueue中获取Message,并使用Handler来处理消息
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值