Android消息机制源码,一个比较直观的简析

1.引子

在上一篇Android消息机制基本原理,我们学习了Android消息机制基本原理和使用方法,看到原理比较复杂,什么Handler啊,Looper啊,MessageQueue啊,但使用消息机制却是十多行代码搞定,感觉跟漫威电影一样,前期渲染反叛如何如何逆天,结果被主角轻松用嘴炮打败。在这里插入图片描述
当然Android开发和电影不同,电影中我们希望主角打败反派过程复杂一点,这样才有戏剧冲突性,但在Android开发中,我们是希望越简单越好,Android消息机制通过精妙的设计,使得使用变得简单了,我们可以通过源码分析一下,看看岁月简单的背后,谁在替我们负重前行。

2.ThreadLocal

有必要介绍一下ThreadLocal,这个概念在日常开发中很少涉及,但在Android消息机制的源码里出现了,如果不了解其功能,会对理解源码形成一定的障碍。

首先要强调的是,ThreadLocal不是一个线程,而是和线程有关的泛型类,该来类提供线程本地变量(thread-local variables)。线程本地变量与普通变量不同,顾名思义,这个变量在每个线程里都有一个独立初始化的变量副本,也就是说,这个变量可以实现线程之间相互隔离,互不干扰。

我们可以通过一个示例来看看效果。

我们声明一个String类型的ThreadLocal变量:

//声明类型String的变量
private ThreadLocal<String> threadLocal = new ThreadLocal<>();

然后创建两个子线程,分别在其中通过set方法设置ThreadLocal,完了之后我们在主线程和两个子线程里分别打印ThreadLocal变量值,完整代码如下:


 package com.test.threaddemo;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    private TextView tshow;
    private Button button;

    private String TAG="ThreadLocal";

    private ThreadLocal<String> threadLocal = new ThreadLocal<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tshow=findViewById(R.id.tvshow);
        button=findViewById(R.id.bClick);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Thread2();
               Thread1();
               
        //主线程ThreadLocal
        threadLocal.set("UI线程的数据");
        Log.d(TAG,"UI线程:"+threadLocal.get());
            }
        });

    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    /**
     * 线程1
     */
    private void Thread1() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                threadLocal.set("线程1的数据");
                Log.d(TAG,"线程1:"+threadLocal.get());
            }
        }).start();
    }

    /**
     * 线程2
     */
    private void Thread2() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                threadLocal.set("线程2的数据");
                Log.d(TAG,"线程2:"+threadLocal.get());
            }
        }).start();
    }
}

运行之后,我们看输出结果,可以看到,虽然是同一个ThreadLocal变量,但不同线程打印出来的ThreadLocal值是不同的,主线程和两个子线程输出在本线程里设置的值。我想有人会说,这是每个线程执行有先后顺序导致的,因为设置后立刻打印,所以并不能确定这是ThreadLocal的功劳,我用一个普通的String变量也可以达到一样的打印效果。
在这里插入图片描述
要验证这个问题比较简单,在主线程里不设置ThreadLocal变量的值,如果ThreadLocal是线程隔离的,那么主线程里的应该为空,否则应该是子线程设置的值,代码调整见下,这里加了一个延时,是为了等子线程都执行完了自己的代码,然后再打印主线程里的ThreadLocal变量。

         Thread2();

         Thread1();


	    //加上延时,确保等子线程代码执行结束
		try {
             Thread.sleep(5000);
            } catch (InterruptedException e) {
                    e.printStackTrace();
            }

         if(threadLocal.get()==null)
         {
           Log.d(TAG,"threadLocal值为空");
         }else{
         	Log.d(TAG,threadLocal.get());
         

代码运行结果如下,我们看到,主线程里的threadlocal的确还是空的,证明了ThreadLocal是能够实现线程隔离的:
在这里插入图片描述
那么ThreadLocal是如何实现这样的功能的呢?

先来看ThreadLocal的set方法的实现

 /**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

粗略理解这6行代码,有以下步骤

  1. 获取当前线程;
  2. 获取当前线程的成员ThreadLocalMap变量map;
  3. 如果map非空,将ThreadLocal和value存入到map中;
  4. 如果map为空,创建map,并将ThreadLocal和value副本放入map中。

看这些流程,感觉是似懂非懂,懂的部分是感觉逻辑上是这么回事,不懂的地方是,getMap做了什么操作,map是什么?createMap又做了什么操作?当你按Ctrl+B点开这些函数定义时,估计得头大一会。看源码碰到这种情况是常见的事,往往看起来只有几行,但嵌套的东西特别多,不了解嵌套的东西,你对源码肯定理解不完整,但要把嵌套的都了解,内容又不少。言归正传,刚说到ThreadLocalMap,简单理解,ThreadLocalMap是一个Hash Map,是存储当前线程数据的Map集合,既然是Map,必定是以键值对存储数据,那么键是什么,值是什么?我们看到map.set方法第一个参数是this,虽然知道this是用来指向当前对象或类实例,但不太确定这个this指的是什么,那么可以看源码的注释部分,会发现官方已经说的比较清楚

Sets the current thread’s copy of this thread-local variable to the specified value
设置当前线程的线程本地变量副本为特定值

根据源码和注释,我们可以确认ThreadLocalMap存储的是ThreadLocal变量副本和对应的值,例如在前边的例子中,我们new了一个ThreadLocal变量,那么ThreadLocalMap存储的便是threadLocal副本,一个线程存一个副本,各个副本之间是相互隔离,这解释了同一个变量在不同线程可以设置不同的值的原因。对这个问题,刚开始我理解成ThreadLocalMap是一个跨线程的map,也就是多个线程共用一个map,以线程的ID作为Key,后来发现这么理解不对,而是每个线程有一个ThreadLocalMap,TheadLocal变量以副本形式存储在每个线程的ThreadLocalMap中。

getMap()函数即获取当前线程的ThreadLocalMap,我看下getMap的定义,代码如下:

 /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

可以看到,代码很简单,就一个返回,我开始看这段代码,纠结t.threadLocals是什么,因为看到函数的返回值类型是ThreadLocalMap,但threadLocals从名字看又像是ThreadLocal啊,怎么和ThreadLocalMap关联起来,后来查其他资料才确定,t.threadLocals就是ThreadLocalMap,证据在Thread的源码,里面有一段代码,如下所示,疑惑的是为何变量起名不带个map,这样意思更明白一点。Thread源码同时还显示了每个线程内部有一个ThreadLocalMap,Thread只是提供这个成员变量,由ThreadLocal对其维护。

public class Thread implements Runnable {
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
}

希望这么多名称没把你绕晕。我们可以用一个图总结Thread,ThreadLocal,ThreadLocalMap的关系,简单来说是Thread里有ThreadLocalMap,ThreadLocalMap由ThreadLocal维护,维护是指创建,管理ThreadLocal变量副本的值。
在这里插入图片描述
关于ThreadLocal简单介绍就到此,我们主要是想知道,ThreadLocal如何做到线程之间相互隔离的,而这个目标已经达到,其实ThreadLocal还有很多内容值得去挖掘,例如怎么设置值,如何删除键值等,有兴趣的读者可以深入了解一下,据说面试官喜欢考察这些内容。

我们再回忆一下Android消息机制,UI线程里有个消息队列MessageQueue,Looper开启无限循环,不断查看消息队列中是否有消息进来,如果有则取出,主线程创建Handler实例给子线程引用,子线程通过Handler发送消息给主线程,其实是把Message压入消息队列中,这样实现了子线程和主线程消息的传递。
在这里插入图片描述

3.Mesage和MessageQueue源码

回忆上一篇博客里提到的传票叉例子,在这个例子中,我们能够有条不紊处理其他家庭成员发来的消息,正是依靠传票叉,同样道理,主线程有条不紊处理子线程发过来的消息,也是依靠消息队列,正因如此,个人认为消息队列是Android消息机制的核心,因此我们从Message,MessageQueue开始。

我们先来看Message对象的成员变量:

成员变量 含义 备注
what 消息类别 用户定义的消息代码,以便收件人可以识别此消息的含义
arg1 参数1 用于传递轻量级数据例如一些整数值
arg2 参数2 用于传递轻量级数据例如一些整数值
obj 消息内容 任意对象,在使用Messenger跨进程传递Message时不能为null
data Bundle数据 比较复杂的数据建议使用该变量
target 消息响应方 关联的Handler对象,处理Message时会调用它分发处理Message对象
when 触发响应时间 处理消息时间
next 下一个Message对象 用next指向下一条Message

我们注意到Message包含一个next的成员变量,该变量指向了下一条Message,next作用是把一系列Message给串联起来,实际上,Message是单链表的节点,每个节点包含该节点的数据和指向下一个节点的数据,单链表样子见图。
在这里插入图片描述
Message只是构成单链表结构,它不能自己管理自己节点的添加和读取,这个工作由MessageQueue完成,其中,节点添加是消息入队,由enqueueMessage()完成,节点读取是消息出队,由方法next()完成.

我们先看enqueueMessage,源码如下:

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            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;
            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 {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

该函数包含两个入参,第一个是消息,第二个是消息的时间。

源码中,首先做两个判断,第一个判断msg.target == null作用是判断负责分发消息的Handler是否为空,第二个判断msg.isInUse()是确认消息是否已使用,已使用表示消息已经被创建并加入队列,但还没读取;接下来进入synchronized代码块,synchronized表示对这段代码加锁,一次只允许一个线程操作,等当前线程处理完毕后下一个线程才能使用,加锁保证子线程排队处理消息入列,不能随便插队。synchronized代码块里的第一句还是做个判断mQuitting,这个判断作用我们可以从打印错误的消息得知,这是判断是不是发送消息到一个死线程上了。

三个判断都通过之后,可以开始执行入队操作了。
在开始之前,还是需要介绍一些概念,首先是mMessage,表示当前待处理的消息,MessageQueue是根据消息的时间顺序处理消息,如果已经存在消息队列,那么mMessage的时间一定消息队列里最早的,when是消息的时间,时间排序即根据when来的。

现在要插入消息,无非是往队列的头部,中部还是尾部插入消息,第一个判断if (p == null || when == 0 || when < p.when)成立表明是要将消息插入队列头部,p==null表示代表MessageQueue没有消息待处理,准备插入消息是第一个消息,when == 0表示待插入的消息需要插入到头部,因为时间是非负值,现在时间设置为0,明摆着是要暴力插队到消息头部,when < p.when表示待插入的消息时间早于mMessage的时间,而mMessage又是当前消息队列里时间最早的,自然也是要插队到队首,我们可以看图来理解如何将消息插到队列的头部,一个将待插入消息msg的next指向mMessage,再一个是将mMessage赋值为待插入的消息即可。
在这里插入图片描述
插入其他位置则是通过循环,从队列头开始遍历,逐一比较待插入消息的时间和其他节点消息的时间,如果早于某个节点时间或者到了队列的尾部,退出循环并执行插入队列操作。

我们再来看看消息出队next()方法,源代码比较长,不一一分析了,消息出队的基本逻辑就是如果当前MessageQueue中存在mMessages,就将这个消息出队,然后让下一条消息成为mMessages,否则进入阻塞状态,一直等到有新的消息入队。另外,消息出队同时也会删除该消息。

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

            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();
                }
                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.
            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.
            nextPollTimeoutMillis = 0;
        }
    }

4.Handler源码

消息入队enqueueMessage由谁调用呢?我们还记得在最上层应用mHandler.sendMessage(msg);来发送消息,我们可以打开这个函数的定义,经过穿越层层封装,最后定位到sendMessageAtTime函数,在里面我们找到了消息入队函数的身影,由于我们已经了解enqueueMessage,再看它时倍感熟悉,sendMessageAtTime有两个入参,第一个是消息,不用过多解释了,第二个是我们知道是时间,但这是什么时间呢,官方注释有答案,这是系统启动到现在这个过程中的处于非休眠期的时间,单位是毫秒。

   public boolean sendMessageAtTime(@NonNull 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);
    }

消息出队又有谁调用?还是一样的思路,我们从上层应用入手,我们在实例化Handler时重写了handleMessage方法,通过查看定义(快捷方式Ctrl+B),定位到Handler源代码里的里的handleMessage,现在再来看谁调用了这个接口,我们在dispatchMessage方法里找到handleMessage的身影,dispatchMessage源码如下。

源码里有两个判断,第一个是判断消息消息有callback,即通过Post(Runnable)的方式投递消息,因为在投递runnable时,把runnable对象赋值给了message的callback,如果有则使用该方法处理消息,第二个是是判断handler的mCallback是否为空,不为空则消息交由callback创建handler方式去处理,如果以上判断都不通过,则使用我们在上层创建handler对象时重写handlerMessage中处理。

接下来应该是去查哪里调用了dispatchMessage,因为无法通过查看引用的方式找到所有调用dispatchMessage,查到的引用不是我们想要的答案,这里直接说答案吧,需要到looper源代码中查找答案。

 /**
     * Handle system messages here.
     */
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

5.Looper源码

在上一篇博客,我们讲到Looper作用是创建无限循环,不断查看是否有消息入队,有消息进来则取出。Looper里开启无限循环是在loop()方法里,对于该方法,官方注释解释道,此线程中运行消息队列,源代码比较长(故附在文末),我们只挑重点的来说。

源代码的第一个重点和消息分发无关,但日常开发会碰到,我们注意到,代码上来判断Looper是否为空,如果为空则抛出No Looper; Looper.prepare() wasn't called on this thread异常,我们去看看Looper.prepare()是干啥子的,注释说的很清楚,该方法是将当前普通线程设置成Looper,这也才能创建一个指向该Looper的Handler实例,在普通线程里没有调用Looper.prepare(),创建Handler是会报错的。细心的读者会发现,我们在Activity例子里并没有调用什么Looper.prepare(),还是可以成功创建Handler实例啊,这又是为什么?这实际是因为Activity已经帮我们调用了,我们可以在Activity里加上Looper.prepare(),肯定会报错Only one Looper may be created per thread,因为重复创建了。

第二个重点还是和消息分发无关,而是在这里总算见到老朋友ThreadLocal,不然上文一大堆ThreadLocal白介绍了。使用ThreadLocal来保存什么?保存Looper对象,这样一个Looper对象和一个线程绑定,使用ThreadLocal的作用有两个,一个是技术上避免线程里重复创建Looper,另一个是不同线程的Looper能够相互隔离。为何要用ThreadLocal保存Looper对象呢?是为了方便Handler调用当前线程的Looper,Handler要发送消息和获取消息,需要知道消息队列,而消息队列由Looper提供,如果没有ThreadLocal,Handler想要找到指定线程的Looper比较麻烦。

 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

 public static void prepare() {
        prepare(true);
    }
    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));
    }

第三个重点终于到我们关心的消息分发。我们跳到代码的无限循环for (;;)部分, 第一句Message msg = queue.next();就是取出消息代码,对,这里就用到了消息队列的next()函数。取到消息后,我们一路追踪msg变量,看在哪里使用了这个变量,最后定位到这句代码msg.target.dispatchMessage(msg);dispatchMessage不就是是Handler源码里提到的分发消息函数嘛,那msg.target又是什么?其实就是Handler,Handler类型的target是Message的一个成员变量,我想为啥在Handler源码里无法通过查看引用找到这里,就是因为调用比较隐晦。

Looper的源码比较长,读者可以自己在Android Studio里查看,阅读体验应该会更好。

 /**
     * 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();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // 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();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;

        for (;;) {
            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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }
            // Make sure the observer won't change while processing a transaction.
            final Observer observer = sObserver;

            final long traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            Object token = null;
            if (observer != null) {
                token = observer.messageDispatchStarting();
            }
            long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
            try {
                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);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
            }

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

6.总结

虽然说源码解析,但也仅仅分析其中一小部分,目的是为了从源码层面看看Handler,Looper,Message,MessageQueue和ThreadLocal在消息机制中如何运作,主要脉络是看消息怎么运转,从哪里来,到哪里去,期间经历了什么。Message是消息的载体,可组成单链表结构的队列,这个队列由MessageQueue管理,例如消息的入队和消息的出队,而MessageQueue又由Looper维护,Handler通过ThreadLocal找到当前线程的Looper,进而找到MessageQueue并实现消息的发送,也就是消息的入队,而消息的出队也就是消息的分发,也是在Looper里使用MessageQueue的next方法,然后通过Handler进行分发。

发布了19 篇原创文章 · 获赞 6 · 访问量 6436

猜你喜欢

转载自blog.csdn.net/lansoul1987/article/details/105156218