Android 9.0 源码_机制篇 -- 全面解析 Handler 机制(原理篇)上

开篇


核心源码

15405197-c8e89e7dec712514.png
123PY`(GH()4}Z9MTYA4M26.png

简述


在整个 Android 的源码世界里,有两大利剑,其一是 Binder 机制,另一个便是 Handler 消息机制。消息机制涉及**MessageQueue/Message/Looper/Handler **这4个类。

Handler 是 Android 中引入的一种让开发者参与处理线程中消息循环的机制。我们在使用 Handler 的时候与 Message 打交道最多,Message 是 Hanlder 机制向开发人员暴露出来的相关类,可以通过 Message 类完成大部分操作 Handler 的功能。

作为一名程序员,我们不仅需要知道怎么用 Handler ,还要知道其内部如何实现的,这就是我写这篇文章的目的。

模型


消息机制(Handler)主要包含:
    ✨ Message:消息分为硬件产生的消息(如按钮、触摸)和软件生成的消息;
    ✨ MessageQueue:消息队列的主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);
    ✨ Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
    ✨ Looper:不断循环执行(Looper.loop),按分发机制将消息分发给目标处理者。

实例


 * A typical example of the implementation of a Looper thread,
 * using the separation of {@link #prepare} and {@link #loop} to create an
 * initial Handler to communicate with the Looper.
 */
class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}
接下来我们就围绕这个实例展开讲解!

一 Looper


消息队列 MessageQueue 只是存储 Message 的地方,真正让消息队列循环起来的是 Looper,我们先来重点分析 Looper。

Looper 是用来使线程中的消息循环起来的。默认情况下当我们创建一个新的线程的时候,这个线程里面是没有消息队列 MessageQueue 的。为了能够让线程能够绑定一个消息队列,我们需要借助于 Looper :首先我们要调用 Looper 的 prepare() 方法,然后调用 Looper 的 Loop() 方法。

需要注意的是 Looper.prepare() 和 Looper.loop() 都是在新线程的 run 方法内调用的,这两个方法都是静态方法。

public static void prepare() {...}
    private static void prepare(boolean quitAllowed) {...}
    public static void loop() {...}

1.prepare()


我们来看一下 Looper.prepare(),该方法是让 Looper 做好准备,只有 Looper 准备好了之后才能调用 Looper.loop() 方法。

public static void prepare() {    
        prepare(true);     // 无参,调用 prepare(boolean quitAllowed)
    }
private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
        // 每个线程只允许执行一次该方法,第二次执行时已有 Looper,则会抛出异常!
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 创建 Looper 对象,并保存到当前线程的本地存储区
        sThreadLocal.set(new Looper(quitAllowed));      
    }

上面的代码首先通过 sThreadLocal.get() 拿到线程 sThreadLocal 所绑定的 Looper 对象,由于初始情况下 sThreadLocal 并没有绑定 Looper ,所以第一次调用 prepare 方法时,sThreadLocal.get() 返回 null,不会抛出异常。

2.ThreadLocal

ThreadLocal:线程本地存储区(Thread Local Storage,简称为 TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。

TLS 常用的操作方法:

3.set()

public void set(T value) {
        Thread t = Thread.currentThread();    // 获取当前线程 
        ThreadLocalMap map = getMap(t);       //查找当前线程的本地储存区
        if (map != null)
            map.set(this, value);             // 保存数据 value 到当前线程 this
        else
            createMap(t, value);
    }
我们看下 getMap() 函数:
 ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
判断 map 是否为空,如果为空则创建 ThreadLocalMap :
 void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

4.get()

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }
Looper 通过如下代码保存了对当前线程的引用:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); // sThreadLocal 为 ThreadLocal 类型

所以在 Looper 对象中通过 sThreadLocal 就可以找到其绑定的线程。ThreadLocal 中有个 set 方法和 get 方法,可以通过 set 方法向 ThreadLocal 中存入一个对象,然后可以通过 get 方法取出存入的对象。

ThreadLocal 在 new 的时候使用了泛型,从上面的代码中我们可以看到此处的泛型类型是 Looper ,也就是我们通过 ThreadLocal 的 set 和 get 方法只能写入和读取 Looper 对象类型。

二 构造函数

源码中 Looper 的构造函数是 private 的,也就是在该类的外部不能用 new Looper() 的形式得到一个 Looper 对象。

private Looper(boolean quitAllowed) {...}
我们看下上面代码中 new Looper() 创建 Looper 对象的工作:
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);  // 创建 MessageQueue 对象
    mThread = Thread.currentThread();        // 记录当前线程
}

Looper.prepare()在每个线程只允许执行一次,该方法会创建 Looper 对象,Looper 的构造方法中会创建一个 MessageQueue 对象,再将 Looper 对象保存到当前线程 TLS。

1.prepareMainLooper()


另外,与 prepare() 相近功能的,还有一个 prepareMainLooper() 方法,该方法主要在 ActivityThread 类中使用。

public static void prepareMainLooper() {
        prepare(false);          // 设置不允许退出的 Looper
        synchronized (Looper.class) {
            // 将当前的 Looper 保存为主 Looper,每个线程只允许执行一次
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

2.loop()

Looper.loop()的代码如下:

 /**
     * 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;    // 获取 Looper 对象中的消息队列

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        // 确保在权限检查时基于本地进程,而不是基于最初调用进程
        final long ident = Binder.clearCallingIdentity();

        for (;;) {     // 进入loop的主循环方法
            Message msg = queue.next(); // might block(可能会堵塞)
            if (msg == null) {          // 没有消息,则退出循环
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            // 默认为null,可通过 setMessageLogging() 方法来指定输出,用于 debug 功能
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
                msg.target.dispatchMessage(msg);       // 用于分发 Message
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();     // 确保分发过程中 identity 不会损坏
            if (ident != newIdent) {
                // 打印 identity 改变的 log,在分发消息过程中是不希望身份被改变的
            }

            msg.recycleUnchecked();   // 将 Message 放入消息池
        }
    }

我们接下来会重点分析 loop() 里面的几个函数:

myLooper()

前面我们说过,在执行完了 Looper.prepare() 之后,我们就可以在外部通过调用 Looper.myLooper() 获取当前线程绑定的 Looper 对象。

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();      // 还是通过 sThreadLocal.get()方法获取当前线程绑定的 Looper 对象
    }

3.MessageQueue

// Looper 构造函数中创建了 mQueue,即 MessageQueue
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);  // 创建 MessageQueue 对象
    mThread = Thread.currentThread();        // 记录当前线程
}
 public static void loop() {
        final Looper me = myLooper();             // 获取当前线程绑定的 Looper 对象
        if (me == null) {
            ... ...
        }
        final MessageQueue queue = me.mQueue;     // 获取 Looper 对象中的消息队列

变量 me 是通过静态方法 myLooper() 获得的当前线程所绑定的 Looper,me.mQueue 就是当前线程所关联的消息队列。

4.for()

for (;;) { // 进入loop的主循环方法

我们发现for循环没有设置循环终止的条件,所以这个for循环是个死循环。

5.Message

Message msg = queue.next(); // might block

我们通过消息队列 MessageQueue 的 next 方法从消息队列中取出一条消息,如果此时消息队列中有 Message,那么 next 方法会立即返回该 Message,如果此时消息队列中没有 Message,那么 next 方法就会阻塞式地等待获取 Message。

6.dispatchMessage()

/*package*/ Handler target;
try {
    msg.target.dispatchMessage(msg);
    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
    if (traceTag != 0) {
        Trace.traceEnd(traceTag);
    }
}

msg 的 target 属性是 Handler,该代码的意思是让 Message 所关联的 Handler 通过 dispatchMessage 方法让 Handler 处理该 Message ,关于 Handler 的 dispatchMessage 方法将会在下面详细介绍。

7.recycleUnchecked()

msg.recycleUnchecked(); // 分发后的 Message 回收到消息池,以便重复利用

小结

loop()进入循环模式,不断重复下面的操作,直到没有消息时退出循环:

1、读取 MessageQueue 的下一条 Message;

2、把 Message 分发给相应的 target;

3、再把分发后的 Message 回收到消息池,以便重复利用。

明天继续更完剩下的


15405197-32dba3f40c1e4760.png
image.png

想学习更多Android知识,或者获取相关资料请加入Android技术开发交流2群:935654177。本群可免费获取Gradle、RxJava、小程序、Hybrid、移动架构、NDK、React Native、性能优化等技术教程!

猜你喜欢

转载自blog.csdn.net/weixin_34293246/article/details/87134563