Handler源码探析

前言

一句话总结 Handler ,它是Android消息机制中一个辅助工具,用于子线程与主线程进行通讯。git

本文的源码将基于Android10,SdkVersion-29面试

1、Handler具体能作什么

1.1 看下源码的注释

  • A Handler allows you to send and process {@link Message} and Runnable
  • objects associated with a thread's {@link MessageQueue}.

Handler让你能够发送和处理与某个线程的MessageQueue(消息队列)相关联的Message(消息)和Runnable对象app

  • Each Handler instance is associated with a single thread and that thread's message queue.

每个Handler实例都关联一个线程和这个线程的MessageQueue(消息队列)异步

  • When you create a new Handler, it is bound to the thread message queue of the thread that is creating it
  • -- from that point on, it will deliver messages and runnables to that message queue and execute
  • them as they come out of the message queue.

当你建立了一个新的Handler时,它就绑定到建立它的线程的MessageQueue(消息队列),从那时起,Handler将Message和Runnable对象传递到消息队列,并在它们从队列中返回的时候处理async

  • There are two main uses for a Handler:ide

  • (1) to schedule messages and runnables to be executed at some point in the future;函数

    安排Message和Runnable在未来某个时间执行工具

  • (2) to enqueue an action to be performed on a different thread than your own.oop

  • 在不一样的线程中执行操做post

1.2 Handler原理解析

源码的注释中写的很明白 Handler是绑定了建立它的线程之后进行处理Messager和Runnable对象的。

首先看Handler构造函数

在Handler构造函数中,能够看到上面注释提到的,建立一个新Handler实例都会关联一个线程和这个线程持有的 MessageQueue,经过 Looper.myLooper()获取当前线程的Looper,拿到Looper以后将Handler中的mQueue赋值

接着写个调用Handler发消息的例子

开启一个新线程调用Handler发送消息的函数,以 sendMessage(Message msg)函数为例,追踪一下这个函数里都作了什么(除了图中的函数, post(Runnable r)也会经过 getPostMessage(Runnable r)把Runnable对象添加到Message对象后调用send函数)。

调用顺序:sendMessage()->sendMessageDelayed()->sendMessageAtTime()->enqueueMessage() 这里重点看一下后面2个函数都作了什么

sendMessageAtTime()这个函数中拿到了实例Handler时的MessageQueue对象,上文提到过的 mQueue,而后调用 enqueueMessage()函数。在这个函数中首先给msg的target变量赋值为当前的Handler对象,接着调用MessageQueue的enqueueMessag()将消息插入到消息队列。

2、MessageQueue

2.1 消息入队

部分源码

boolean enqueueMessage(Message msg, long when) {
 synchronized (this) {
 //对消息的从新排序,经过判断消息队列里是否有消息以及消息的时间对比
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        //把刚进入消息队列的消息置为消息队列的第一条消息,唤醒队列
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            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; // invariant: p == prev.next
            prev.next = msg;
        }

        // We can assume mPtr != 0 because mQuitting is false.
        //调用nativeWake,以触发nativePollOnce函数结束等待
        if (needWake) {
            nativeWake(mPtr);
        }
    }
复制代码

}

消息入列就是根据Message的when属性的大小进行排序,先执行的放在队列的前面或者遍历消息队列,把当前进入的消息放入合适的位置。

2.2 消息出队

部分源码

```

 Message next() {
 // Return here if the message loop has already quit and been disposed.
    // This can happen if the application tries to restart a looper after quit
    // which is not supported.
    //若是退出消息消息循环,那么就直接退出
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
         //执行native层消息机制层,
        //nextPollTimeoutMillis参数为超时等待时间。若是为-1,则表示无限等待,直到有事件发生为止。
        //若是值为0,则无需等待当即返回。该函数可能会阻塞
        nativePollOnce(ptr, nextPollTimeoutMillis);
        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 && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                //判断是否有消息屏障,同时获取消息队列下一个异步消息
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                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 {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        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;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }

            // If first time idle, then get the number of idlers to run.
            // Idle handles only run if the queue is empty or if the first message
            // in the queue (possibly a barrier) is due to be handled in the future.
            
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            //若是空闲的Ddel handler个数为0,继续让线程阻塞
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }

        // Run the idle handlers.
        // We only ever reach this code block during the first iteration.
        //咱们只会在第一次迭代时到达此代码块
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null; // release the reference to the handler

            boolean keep = false;
            try {
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }

            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }

        // Reset the idle handler count to 0 so we do not run them again.
        将空闲处理程序计数重置为0,再也不运行。
        pendingIdleHandlerCount = 0;

        // While calling an idle handler, a new message could have been delivered
        // so go back and look again for a pending message without waiting.
        唤醒Native消息机制层
        nextPollTimeoutMillis = 0;
    }
}
复制代码

到这里能够看到 MessageQueue是消息的管理者,提供了入队和出队的方法,具体的调用是在 Looper进行的。
ps:说实话这部分源码看的懵懵的,这块须要去深刻了解一下Native消息机制

3、Looper

3.1 Looper是如何建立的

以前看Handler构造函数的时候,能够发如今实例化的时候,会判断当前线程mLooper是否存在,也就是说建立一个Handler时也要建立一个Looper。

mLooper = Looper.myLooper();
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }

经过这个函数得到当前线程的Looper,在主线程中系统已经帮咱们建立好了。

ActivityThread就是Android主线程或UI线程,在ActivityThread的 main()函数中Looper会经过Looper.prepareMainLooper()函数建立sMainLooper

Looper建立完成以后,在main()中会调用 Looper.loop()构建消息循环。

子线程中建立Handler

3.2 Looper如何与主线程关联的

private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));//建立Looper对象,放入ThreadLocal中。这里的boolean参数详见3.3 }


ThreadLocal的做用
源码注释

  • This class provides thread-local variables. These variables differ from
  • their normal counterparts in that each thread that accesses one (via its
  • {@code get} or {@code set} method) has its own, independently initialized
  • copy of the variable. {@code ThreadLocal} instances are typically private
  • static fields in classes that wish to associate state with a thread (e.g.,
  • a user ID or Transaction ID).

ThreadLocal提供线程局部变量。这些变量与正常变量不一样,每个线程访问自身的局部变量时,有它本身的,独立初始化的变量副本。ThreadLocal变量一般是与线程关联的私有静态字段(例如user ID或Transaction ID)。

回过头来看一下sThreadLocal.set(new Looper(quitAllowed))set(T valye)函数

获取到了当前线程,而后获取当前线程的ThreadLocalMap,若是map不为空,直接调用map.set赋值,不然新建一个ThreadLocalMap。这里就不继续向下看了。

总结一下,Looper是经过ThreadLocal来关联主线程的,而且经过ThreadLocal储存保证其线程的私有性。其实不光是主线程,每一个线程的Looper都是这样关联的。

3.3 Looper和MessageQueue怎么关联的,循环能够退出吗?

Looper既然能够开始构建消息循环,那么必然提供了退出的函数。
首先看下Looper的构造函数

在构造函数的参数能看到,在Looper内部建立了一个MessageQueue对象与之关联,还有一个很明显的参数 quitAllowed,是否容许退出。这时须要去看下这个boolean变量在MessageQueue是怎么用到的

这里发现退出循环的方法也是MessageQueue给Looper提供的,在Looper中调用
public void quit() { mQueue.quit(false); }停止循环。
注意: 3.2提到的boolean参数,在主线程中建立Looper时, prepare(false)传入的是flase,主线程是不容许Looper退出的。缘由也很好理解,退出就意味着App挂了。子线程建立Looper默认传入是true

3.4 Looper的消息循环

/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */

public static void loop() {
    final Looper me = myLooper();//获取当前线程的Looper
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;//获取当前线程的消息队列
    省略部分代码

    for (;;) {//循环获取消息队列中的消息
        Message msg = queue.next(); // might block 调用上文提到的MessageQueue提供的next()方法,
        此方法可能会阻塞
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
      省略部分代码
      long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
        try {
        //调用Handler的dispatchMessage函数分发消息(Message对象)
            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);
            }
        }
          省略部分代码
        }

        msg.recycleUnchecked();
    }
}

 总结一下就是无条件循环获取MessageQueue中的消息,而后经过msg.target(为Handler对象)调用
 dispatchMessage函数进行消息的分发。最终又回到了Handler处理消息
复制代码

Handler.dispatchMessage(msg)

这里引用一张图流程图吧

最开始写了个例子发送了消息,如今须要重写handerMessage函数处理消息

Handler Looper Message MessageQueue 关系图解

Handler经过sendMessage()发送Message到MessageQueue队列; Looper经过loop(),不断提取出达到触发条件的Message,并将Message交给target来处理; 通过dispatchMessage()后,交回给Handler的handleMessage()来进行相应地处理。 将Message加入MessageQueue时,处往管道写入字符,能够会唤醒loop线程;若是MessageQueue中没有Message,并处于Idle状态,则会执行IdelHandler接口中的方法,每每用于作一些清理性地工做。

四.相关知识点总结

1.Handler持有MessageQueue对象的引用,经过enqueueMessage函数将消息入队,同时也持有Looper对象的引用,经过Looper.loop()构建消息循环。Looper持有MessageQueue的对象引用,调用MessageQueue的next()无限取出消息交给Handler的dispatchMessage()函数处理
2.Looper能够调用MessageQueue提供的quit(boolean safe)函数退出,可是主线程的 Looper 不容许退出。
3.子线程用Handler,必须调用Looper.prepare() 函数而且调用Looper.loop()或者在实例化的时候传入Looper.getMainLooper()。
4.ThreadLocal每一个线程有单独一份存储空间,它具备线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问到想要的值。

总结这篇也是由于最近在准备面试,但愿能从新梳理一下Android的消息机制。
Handler面试总结

感谢

GityuanAndroid消息机制1-Handler(Java层)
AndyJenniferAndroid Handler机制
秉心说深刻理解 Handler 消息机制

相关文章
相关标签/搜索