Android Handler源码实现解析

489 阅读7分钟

一、写在前面

  在Android中Handler主要被用于线程间切换,比如在子线程中访问网络数据之后需要将数据更新到View中则需要使用到Handler进行数据处理。

1、涉及到的类

  本篇文章会对如下几个类所涉及到的关键函数进行解析,以对Handler底层实现原理进行学习。

  • ActivityThread.java:当前应用进程启动的时候会调用到该类的main函数,在该函数中会创建UI线程的Looper对象并调用其loop函数以启动无限循环防止进程退出;
  • Looper.java:死循环调用MessageQueue中的next函数;
  • MessageQueue.java:Message入队列、出队列处理类,Looper中无限循环调用被阻塞就发生在该类的next函数中;
  • Handler.java:本篇文章主角类。

2、Handler简单使用

  如下以在UI线程中创建Handler为例,对Handler的使用进行简单的学习。

2.1 Handler定义

static class MyHandler extends Handler {
    @Override
    public void handleMessage(@NonNull Message msg) {
        ......
        //业务逻辑,当前逻辑处理则发生在UI线程中
    }
}

2.2 Handler使用

//以UI线程Looper对象创建Handler,如果在子线程使用Handler则可以先创建HandlerThread对象然后通过HandlerThread对象获取Looper对象
Handler handler = new MyHandler();
Message message = handler.obtainMessage();
message.what = 1;
message.obj = new Object();
//该函数调用则可以发生在子线程中
handler.sendMessage(message);

二、源码

  如下以ActivityThread->Looper->MessageQueue->Handler的顺序对其中所涉及到的关键源码进行解析,当然有需求的也可以逆序看。

1、ActivityThread.main

  该函数作为当前应用进程的入口函数(参考Android应用进程启动源码),其首先通过调用Looper.prepareMainLooper函数为当前进程的UI线程创建Looper对象,最后通过调用Looper.loop函数以防止进程无任务处理而退出,同时等待Handler发送Message进行处理。部分源码如下:

public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
    ......
    //为当前进程UI线程创建Looper对象
    Looper.prepareMainLooper();
    ......
    //防止进程死亡,同时等待Handler发送Message进行处理
    Looper.loop();
    //loop意外退出,一般情况下不会出现
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

2、Looper

2.1 preparexx

  通过调用如下函数中的其中一个则可以为当前线程成功创建Looper对象,并将创建好的Looper对象保存到ThreadLocal对象中。

  ThreadLocal:线程内部数据存储类。通过该类可以在指定的线程中存储数据,并只能在存储了对应数据的线程中才能正确获取到对应数据,其他线程则无法获取到该数据。

public static void prepareMainLooper() {
    //调用prepare函数创建Looper对象并保存到ThreadLocal对象中
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        //从ThreadLocal对象获取Looper对象
        sMainLooper = myLooper();
    }
}
public static void prepare() {
    prepare(true);
}
//quitAllowed:表示是否允许退出当前Looper,对于UI线程中的Looper而言则不允许退出
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //创建Looper对象并保存到ThreadLocal对象中
    sThreadLocal.set(new Looper(quitAllowed));
}

2.2 构造函数

private Looper(boolean quitAllowed) {
    //创建MessageQueue对象给Handler使用
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

2.3 myLooper

//获取当前线程对应的Looper对象
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

2.4 loop

  当前线程(比如UI线程)通过调用该函数以防止进程退出。代码比较简单,核心就是通过for启动无限循环并调用函数loopOnce。

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.");
    }
    if (me.mInLoop) {
        Slog.w(TAG, "Loop again would have the queued messages be executed"
                + " before this one completed.");
    }
    ......
    me.mSlowDeliveryDetected = false;
    //死循环不断调用loopOnce函数
    for (;;) {
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}

2.5 loopOnce

  如下代码省略掉了大部分不相关的函数实现。核心逻辑就是调用到函数MessageQueue.next(该函数会阻塞当前线程)以获取下一个能够处理的Meesage,并最终调用到Handler中进行业务逻辑处理。注意该处调用是发生在创建Looper对象的线程中(比如UI线程),这也是能够实现线程切换的重点。

private static boolean loopOnce(final Looper me,
        final long ident, final int thresholdOverride) {
    Message msg = me.mQueue.next();
    //如果没有Message需要处理并且没有被阻塞,则说明上层业务方主动调用了quit函数并且当前looper能够退出
    //则结束当前Looper
    if (msg == null) {
        return false;
    }
    ......
    try {
        //target对应Message的Handler对象,最终会调用到handleMessage函数
        msg.target.dispatchMessage(msg);
        if (observer != null) {
            observer.messageDispatched(token, msg);
        }
        dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
    } catch (Exception exception) {
        if (observer != null) {
            observer.dispatchingThrewException(token, msg, exception);
        }
        throw exception;
    } finally {
        ThreadLocalWorkSource.restore(origWorkSource);
        if (traceTag != 0) {
            Trace.traceEnd(traceTag);
        }
    }
    ......

    return true;
}

3、MessageQueue

3.1 next

  如下代码做了如下几件事情:(1)判断当前MessageQueue是否为空,如果为空则设置下次等待时间为-1,即永久等待直到被唤醒为止;(2)如果不为空则判断队列首部Message是否到达了处理时间,如果到了则立马返回进行处理,否则计算等待时间间隔,重新等待。

Message next() {
    final long ptr = mPtr;
    //判断是否初始化成功
    if (ptr == 0) {
        return null;
    }

    int pendingIdleHandlerCount = -1;
    //阻塞时间,这里有三个值
    //(1)0:不阻塞
    //(2)-1:阻塞,直到被唤醒为止
    //(3)>0:阻塞特定时间之后自动唤醒
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
        //首次进来则不阻塞
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            //获取当前Message队列
            Message msg = mMessages;
            //一般情况下msg.target不会为空,因此这里暂时不看
            if (msg != null && msg.target == null) {
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                //判断当前时间是否小于队列首部Message时间,如果小于则说明不存在Message需要处理
                //注意Message队列首部时间最小,越往后时间越晚
                if (now < msg.when) {
                    //获取最近需要处理Message时间距离当前时间间隔,以计算下次需要等待时间
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                //存在Message到达处理时间
                } else {
                    mBlocked = false;
                    //目前分析情况下prevMsg为Null
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        //移除即将被处理的队列头部Message
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    //设置当前Message正在被处理flag
                    msg.markInUse();
                    //返回队列首部Message进行处理
                    return msg;
                }
            //Message队列为空,因此将等待时间设置为-1(即永久等待直到被唤醒为止)    
            } else {
                nextPollTimeoutMillis = -1;
            }

            if (mQuitting) {
                dispose();
                return null;
            }
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                //说明需要其他地方来进行唤醒,比如首个Message进入队列的时候
                mBlocked = true;
                continue;
            }
            ......
        nextPollTimeoutMillis = 0;
    }
}

3.3 enqueueMessage

  如下代码主要就是按照时间先后顺序将当前Message插入到MessageQueue的指定节点处。MessageQueue中的节点Message对应处理时间是头部->尾部依次递增的。

boolean enqueueMessage(Message msg, long when) {
    //不允许Message对应Handler对象为null
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }

    synchronized (this) {
        //当前Message正在被处理或者正在入队列,在上述next函数中有相关标记代码
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }
        //当前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;
        }
        //表示当前Message正在入队列
        msg.markInUse();
        //Message处理时间,用于后续入队列以及设置等待时间
        msg.when = when;
        //当前Message队列
        Message p = mMessages;
        //是否需要主动唤醒next函数中的阻塞
        boolean needWake;
        //说明当前Message是第一条Message,因此需要主动唤醒
        if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            //p.target一般不会为null,因此这里为false
            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;
        }

        //判断是否主动唤醒next函数中的阻塞,第一次进来一般都需要主动唤醒
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}

4、Handler

4.1 enqueueMessage

  Handler中有多个函数都可以将Message插入到MessageQueue中,不过最终都会调用到enqueueMessage函数中。代码如下:

/**
* queue:从当前线程Looper对象中获取
* msg:需要入队列的Message对象
* uptimeMillis:Message处理时间,SystemClock.uptimeMillis() + 延迟时间,或者业务方传递
*/
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
        long uptimeMillis) {
    //当前Handler对象
    msg.target = this;
    msg.workSourceUid = ThreadLocalWorkSource.getUid();
    //默认为false
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //将Message加入到MessageQueue中
    return queue.enqueueMessage(msg, uptimeMillis);
}

4.2 dispatchMessage

  如下代码就是Looper.loopOnce最终调用到的代码了。其首先会判断处理的Message对象和当前Handler对象是否存在CallBack,如果存在则优先调用CallBack对象,否则调用到当前Handler的handleMessage函数。

public void dispatchMessage(@NonNull Message msg) {
    //判断Message是否设置了CallBack,通过调用Handler中的postxx函数进行设置。
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //判断当前Handler是否设置了CallBack(可通过Handler构造函数传入,hook ActivityThread中Handler对象重点)
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //如果Message和Handler都没有设置CallBack则调用到handleMessage,后续就走到了我们的业务逻辑。
        handleMessage(msg);
    }
}

三、总结

  如上即为Handler底层大致源码了,其能够实现线程间切换的主要原理就是创建Looper对象的线程(比如UI线程)会不断循环(如果没有Message或者Message处理时间未到则会被阻塞)读取MessageQueue中是否有需要处理的Message,如果有则将需要处理的Message发送给Handler进行处理;而其他子线程则只需要通过对应的Handler对象将需要处理的Message插入到MessageQueue即可。

  总之一句话,Handler中处理的业务逻辑最终运行在哪个线程中,取决于创建Handler时传递进去的Looper对象在哪个线程中被创建并调用其loop函数。