从源码角度简析 Android 消息机制

MessageQueue 源码解析

MessageQueue 直译过来就是消息队列的意思,但是实际上 MessageQueue 的底层实现不是 Java 中的不是用的队列,而是使用一个单链表,多说无益,我们从代码的角度来一探究竟。打开 MessageQueue 源码,首先是类的介绍 ——

《从源码角度简析 Android 消息机制》

直译过来大概就是:

一个持有通过 Looper 来发送消息队列的低级类,Message 对象不会立即添加进 MessageQueue,而是通过与 Looper 关联的 Handler 对象来添加。
你可以通过 Looper 的 myQueue() 方法找到当前线程的 MessageQueue 对象

这里先暂时放在这,我们再来看一看 MessageQueue 的进出队列方法,毕竟 MessageQueue 在消息机制中扮演消息队列的角色,其最重要的方法当然就是进出队列方法了,首先看进入队列的方法 ——

enqueueMessage()

《从源码角度简析 Android 消息机制》

关键点在第565~576行代码间,我们可以看到我们将新传进来的 Message 对象是根据 when 来放入的,最终这个 Message 对象所处的位置的前一个 Message 对象的 when 值比它小(也就是先于它被去除),同时我们也可以看到,对于一个 Message 对象来说,它内部是使用 next 变量持有下一个 Message 对象的引用,这里我们可以打开 Message 的源码查看到,源码如下:

《从源码角度简析 Android 消息机制》

所以我们前面所提到的 MessageQueue 对象底层实现其实就是一个单链表原因就在此。

解决完入队方法,我们再来看看出队方法,MessageQueue 中的出队方法源码如下:

next()

《从源码角度简析 Android 消息机制》

我们看到318行代码处,for 循环是一个死循环,如果消息队列中没有消息,那么循环将会一直阻塞在这里,而如果有消息的话,如代码344~352行代码处所示,将该消息对象移除单链表并且返回该 Message 对象。

到此 MessageQueue 源码解析就暂时告一段落了,接下来看一下 Looper 源码的解析。

Looper 源码解析

打开 Looper 类源码,类的注释如下图——

《从源码角度简析 Android 消息机制》

直译如下:

一个使线程内消息循环的类,默认情况下线程并没有启动小心循环;创建了一个新的 Looper 对象,需要调用它的 prepare() 方法来开始使得消息循环,然后 loop() 方法就会一直处理消息,直到循环停止。

大部分的消息循环交互是通过 Handler 类实现的。

这里有一个经典的实现 Looper 的线程的例子,使用 prepare() 和 loop() 两个方法让 Looper 能够和普通的 Handler 交互。

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.prepare() 方法才能给当前线程创建 Looper 对象,并且需要调用 Looper.loop() 方法才能让当前线程的 MessageQueue 中的消息循环被处理。这里可能有小伙伴有疑惑,为什么需要使用 Looper.prepare() 来创建一个 Looper 而不是 new Looper() 呢?这个时候我们不妨打开 Looperprepare() 方法,源码如下 ——

prepare()

《从源码角度简析 Android 消息机制》

根据源码我们可以看到,先从 ThreadLocalget() 一下,如果当前 get() 的值为 null,那么我们就给 ThreadLocal 对象 set 一个 new Looper() 对象,我们再定位到 Looper 的构造方法看一下 ——

《从源码角度简析 Android 消息机制》

原来 Looper 的构造方法是私有的,不对外暴露的,所以我们不能够使用 new Looper() 来创建一个 Looper 对象,所以我们还需要注意一个知识点就是 —— 将构造方法私有化的目的就是对于一个线程来说它只能有一个 Looper,那么如何实现只有一个 Looper 呢?答案就是通过使用 ThreadLocal 类来实现,如果不了解 ThreadLocal 类的话,可以参考我的另一篇博客从实例和源码角度简析 ThreadLocal。这里我们看到,Looper() 对象在创建的时候,同时会初始化 MessageQueue 并且保存对当前线程的引用。

解决完 prepare() 方法后,我们再来看看 loop() 方法,源码如下:

loop()

《从源码角度简析 Android 消息机制》

我们定位到135行代码可以发现又是一个 for 的死循环,Looper 不停地从消息队列中拿出消息并处理,而想要退出死循环的唯一方法就是第137~140行代码所以,除非 Message 对象为空,才能退出消息循环,而 Message 对象非空的情况下都会走到第154行代码 —— msg.target.dispatchMessage(msg),也就是调用 Handler 对象的 dispatchMessage() 方法(关于 Message 对象的 target 字段可参考send() 系列)。处那么如何才能让 Message 对象为空,或者说怎样才能退出消息循环呢?我们可以看到 loop() 方法上方的注释说到 —— 请确认调用了 quit() 方法来退出循环,所以关键点就是在 quit() 方法了,事实上不止 quit() 方法,Looper 同时还提供了 quitSafely() 方法,所以如果使得消息不再循环的关键点就在这两个方法上了,我们看看这两个方法的源码,源码如下:

quit()、quitSafely()

《从源码角度简析 Android 消息机制》

这里的注释就不详细翻译了,我们查看230~231行的注释可以了解到,调用了 quit() 方法之后 loop 将会立即停止,无论消息队列中是否还有消息;而250~253行代码告诉我们,调用 quitSafely() 方法,会将消息队列中所有的非延迟消息(没有使用 sendMessageDelayed()postDelayed() 的方法)处理完再退出循环。那么具体是怎么做的呢?我们可以看到是调用了 MessageQueue 对象的 quit(boolean) 方法,一个是 false,一个是 true。那么我们就再来看看 MessageQueuequit(boolean) 方法,源码如下:

《从源码角度简析 Android 消息机制》

查看424~427行的代码我们可以看到,如果 safe 值为 true 则走 removeAllFutureMessagesLocked() 方法,否则就走 removeAllMessagesLocked() 方法(其实从方法名我们就可以看出来,一个是移走将来的消息,一个是移走所有的消息),也就是说 Looperquit() 方法会走下面的 removeAllMessagesLocked() 方法,而 quitSafely() 方法会走上面的 removeAllFutureMessagesLocked() 方法,其实这里我们可以联想到,removeAllFutureMessagesLocked() 方法终将会调用 removeAllMessagesLocked() 方法的,因为当当前消息队列中存在延迟消息的时候,那么这个时候无论 safe 参数的值是否为 true,它们的处理方式应该如出一辙,猜想到这,我们查看一下两个方法的源码:

《从源码角度简析 Android 消息机制》

如猜想假设的一样,第736~738行代码告诉了我们,如果当前消息对象是延迟消息的话,那么我们就调用 removeAllMessagesLocked 方法移除所有的消息(根据前面的 MessageQueue 源码解析我们可以发现 MessageQueue 队列底层的实现是一个有序的单链表,而这个单链表的排序就是按照 Message 对象的when 值来递增排序的,所以如果当前 Message 对象的 when 值大于当前系统时间,那么其后的所有 Message 对象的 when 值必定大于当前系统时间),而如果当前消息并非延迟消息的话,那么我们就往后找,找到延迟消息并回收它以及它往后的所有消息对象。

至此,Looper 源码解析完了。

Handler 源码解析

打开 Handler 源码,Handler 源码注释太长了,这里就不翻译和截图了,我们直接对它里面中的方法进行剖析,首先先从 Handler 的构造函数入手,Handler 一共提供了如下几种构造方法 ——

构造方法

《从源码角度简析 Android 消息机制》

目前笔者接触的比较多的是前四种构造函数,至于后三种,由于笔者能力有限,就在此处不扩展了,前四种构造函数方法,最终调用的都是如下的两种当中其一的构造函数 ——

《从源码角度简析 Android 消息机制》

《从源码角度简析 Android 消息机制》

代码还是非常好理解的,如果传入一个 Looper 对象那么就就在构造函数中保存这个 Looper 对象的引用,而如果没有传入 Looper 对象的话,那么就使用 Looper.myLooper() 获取当前线程的 Looper 对象(这个在前面的 Looper 源码中已经分析过了),然后就是对传入的 Callback 对象保存引用,当然,这个 Callback 对象是可以为 null 的。那么这个 Callback 对象又是什么呢?

《从源码角度简析 Android 消息机制》

73~79行的注释说到 —— 可以使用通过实现 Callback 接口来实例化一个 Handler 对象而不用去自己去实现 Hanlder 的子类。这就非常好理解了,我们日常中创建 Handler 对象的方式常是构造一个匿名内部类重写 Hanlder 中的 handleMessage(Message) 方法,代码类似如下:

private static class InnerHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        // do something
    }
}

而我们如果通过实现 Callback 接口的话就可以使用另一种方法了 ——

private static Handler mHandler = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            // do something
        }

        return true;
    }
});

当然,这两个 handleMessage 还有一个区别就是重写父类的话该方法是无返回参数的,而如果是实现 Callback 接口的话是返回一个 boolean 值的,看第78行源码说到 —— 如果没有想进一步处理的期望的话那么就返回 true。这是个什么意思呢?这个在 dispatchMessage() 中我们会详细谈到,对 Callback 的说明在这里先暂告一段落。

send() 系列

下面我们来看 Hanlder 中的另一个重点 —— 发送消息机制 —— send() 方法系列。实际项目开发中发送消息时我们可能会使用到这种调用 api 的形式 —— mHandler.sendEmptyMessage(0x123);Hanlder 给我们提供了丰富的 api 供我们调用,共有七种方法,如下:

《从源码角度简析 Android 消息机制》

其中第七种方法在使用中并不常见,限于笔者能力有限,对第七种方法就不进行扩展了,而上面的六种方法最终也会走入 sendMessageAtTime() 方法,而事实上 sendMessageAtTime() 方法先是调用 enqueueMessage() 方法再是会调用 MessageQueueenqueueMessage() 方法将消息压入队列,源码如下:

《从源码角度简析 Android 消息机制》

《从源码角度简析 Android 消息机制》

Hanlder 类的 enqueueMessage() 方法中,当前 Handler 对象赋值给了 Message 对象的 target 字段。

post() 系列

Handler 除了提供大量的 send() 系列方法供我们使用同时还提供了大量的 post() 方法可供我们使用 ——

《从源码角度简析 Android 消息机制》

同样的,日常开发中更多使用的是前四种 post() 方法,那么什么是 post() 方法呢?我们查看一下 post() 方法的源码:

《从源码角度简析 Android 消息机制》

我们惊奇地发现,post() 方法内部实际上竟然调用的是 send() 系列方法(post() 系列其他方法也是大同小异,此处就不再放源码了)!关键点就在于 getPostMessage() 方法将一个 Runnable 对象转换成了 Message 对象,这是怎么一回事呢?我们不妨打开 getPostMessage() 源码 ——

《从源码角度简析 Android 消息机制》

我们在这里就看到了关键的地方,原来 Message 类中有一个 callback 字段,其类型就是 Runnable 的,所以 post() 方法相比于 send() 方法对比,无非是给 Message 对象的 callback 字段赋了值。

dispatchMessage()

send() 系列和 post() 系列都是发送消息,而 Handler 的处理消息方法就是 dispatchMessage() ,源码如下:

《从源码角度简析 Android 消息机制》

逻辑还是相当简单的,第一个分支判断,Message 对象的 callback 字段是否为空,那么 Message 对象的 callback 字段是什么?通过 post() 系列源码分析我们知道了 Message 对象的 callback 字段实际上就是通过 post(Runnable) 系列方法传入的 Runnable 接口,也就是说如果我们使用了 post(Runnable) 系列方法发送消息的话,该 callback 字段就不会为空,那么就调用 handleCallback() 方法,那么我们来看一下 handleCallback(Message) 的实现 ——

《从源码角度简析 Android 消息机制》

很简单,也就是调用传入的 Runnable 接口的 run() 方法。

而如果我们是通过 send() 系列方法来发送消息的话,那么这个 callback 字段就是为空的。那么就会经过下面的分支,首先会判断 mCallback 是否为空,那么 mCallback 又是一个什么参数呢?mCallback 就是我们之前构造函数中提到的那个仅含有一个 boolean handleMessage() 的那个接口,如果这个接口不为空,且返回 true,那么就结束整个分发的流程,如果这个 mCallback 为空或者或 Callback 接口的 handleMessage 方法返回 false,那么就会执行 handleMessage() 方法 —— 也就是我们最常使用的匿名内部类当中的那个 handleMessage() 方法。下面来看一个例子 ——

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "TAG";
    private static Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case 0x123:
                    Log.e(TAG, "Handler.Callback.handleMessage()");
                    break;
                default:
                    break;
            }

            return false;
        }
    }) {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 0x123:
                    Log.e(TAG, "handleMessage()");
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button getButton = (Button) findViewById(R.id.btn_get);

        getButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread() {
                    @Override
                    public void run() {
                        mHandler.sendEmptyMessage(0x123);
                    }
                }.start();
            }
        });
    }
}

这个代码其实就是为了解答我们前面在构造方法中提出的那个问题,如果搞懂了上面的 dispatchMessage 流程的话,这里就应该知道,答案输出如下 ——

《从源码角度简析 Android 消息机制》

原因就在走 else 分支的时候它会判断当前的 mCallback 是否为空,如果不为空那么就执行它的 handleMessage 方法,并且如果这个方法如果返回为 true 的话那么就直接 return 结束当前的事件处理,而如果是 false 的话那么就会继续执行下面的程序,也就是 Hanlder 自身的 handleMessage 方法。

流程一览

Handler 创建时需要当前线程的 Looper 已经创建好是因为 Handler 的构造方法当中会调用 Looper.myLooper() 方法来获取当前线程的 Looper,我们需要使用 Looper.prepare() 来给当前线程创建一个 LooperLooper 创建的时候同时也会创建一个 MessageQueue 并且保存当前线程对象的引用,MessageQueue 是一个消息队列,但其实质就是一个单链表,它有进队的 enqueueMessage() 方法(该方法有序)放入 Message 对象,也有出队的 next() 方法。然后应该调用 Looper.loop() 方法(一个死循环方法,除非消息队列中没有消息对象了,否则一直调用消息队列的 next() 方法使得消息出队被处理),这样就可以开始对消息队列进行循环取出了。当我们调用 send() 系列的方法或 post(Runnable) 方法(实际上 post() 方法后面也是调用 send() 方法,它唯一多做了一个地方就是将传入的 Runnable 对象赋给了 Message 对象的 callback 字段)发送一个消息对象的时候先是通过 enqueueMessage() 方法将 Handler 对象值赋给 Message 对象的 target 再通过 MessageQueueenqueueMessage() 方法将 Message 对象塞入了 MessageQueue,然后 Looper 就通过 loop() 就开始不停地调用 MessageQueue 对象的 next() 方法取出 Message 对象,并使用 Message 的 target(实质是一个 Handler) 对象的 dispatchMessage 方法,而该方法先分为两个部分,首先判断该 Message 对象的 callback(实质就是 Runnable) 对象是否为空,如果不是为空的话,我们就调用该 Runnable 接口所实现的 run() 方法,如果为空的话,我们就会判断一下另一个 Callback 对象是否为空,这个 Callback 对象实质是就是一个含有唯一一个方法,该方法名为 boolean handleMessage() 的接口,如果这个 Callback 对象不为空,就调用该对象的 boolean handleMessage() 方法,并根据返回值判断是否调用 Handler 本身的 handleMessage() 方法。

Handler 最佳写法

我们可能在日常开发中有以下的陋习 ——

private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 0x123:
                Log.e(TAG, "handleMessage()");
                break;
            default:
                break;
        }
    }
};

《从源码角度简析 Android 消息机制》

大致意思就是告诉我们 Handler 应该被声明为 static 的,否则可能会造成内存泄露。事实上这句话可能大家已经在网上看到过很多遍了,但是并没有什么人去解释为什么,下面听我一一道来 —— Handler 作为一个 Activity 中的一个匿名内部类,它是对 Activity 一直都会保持一个引用的(匿名内部类会持有对外部类的引用),而 Hanlder 的生命周期一般情况下比声明的 Activity 要长,所以当 Activity 需要被销毁的时候,Handler 还存在对它的引用,此时就会造成内存泄露,那么为什么 Handler 的生命周期一般要长于声明它的 Activity 的生命周期呢?我们知道 Handler 内部是通过 Looper.myLooper() 获取到了 Looper 对象的值的,而 Looper 对象我们又是通过 ThreadLocal 获得的 ——

《从源码角度简析 Android 消息机制》

所以实际上 Looper 的生命周期是和当前线程的生命周期一样长的(ThreadLocal 的作用域即是以线程为单位的)。所以实际上 Handler 的生命周期与声明它的那个线程的生命周期是一致的。所以假如我们在 Activity 的主线程中声明了一个 Handler,那么 Handler 的生命周期将会和主线程的生命周期相吻合,所以当我们的 Activity 销毁时,Handler 并不会被销毁,就会导致内存泄露。那么有什么方法来解决呢?既然匿名内部类不行,是因为匿名内部类持有了外部类的引用,那么我们就不妨使用静态内部类,因为静态内部类并没有包含对外部类的引用 ——

public class MainActivity extends AppCompatActivity {
    private Handler mHandler = new InnerHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    private void handleMessage() {
        // do something
    }

    private static class InnerHandler extends Handler {
        private WeakReference<MainActivity> mActivity;

        private InnerHandler(MainActivity activity) {
            mActivity = new WeakReference<MainActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = mActivity.get();
            if (activity != null) {
                switch (msg.what) {
                    case 0x123:
                        activity.handleMessage();
                        break;
                    default:
                        break;
                }
            }
        }
    }
}

我们在静态内部类中持有外部类的一个弱引用,需要使用的时候判断一下该弱引用是否为空,如果为不为空的话就执行 Acitivty 中的 handleMessage() 方法,这样就可以避免内存泄露了。

补充

至于为什么在主线程中不需要使用 Looper.prepare()Looper.loop() 方法,事实上绝大数的技术文章都已经做过了说明,也就是在 ActivityThread 主线程的入口方法 mian() 方法已经调用了 Looper.prepareMainLooper()Looper.loop() 方法创建和开启了主线程的消息循环。

《从源码角度简析 Android 消息机制》

Looper 源码中我们可以看到,关于 Looper 的获取有两种方式,一种是 Looper.getMainLooper(),也就是获取主线程的 Looper,另一种是 Looper.myLooper(),也就是通过 ThreadLocal 获取当前线程的 Looper,所以一个线程有且仅有一个 Looper(如果不清楚可以查看我的另一篇博客blog.csdn.net/ziwang_/art…“>从实例和源码角度简析 ThreadLocal);MessageQueue 的创建实际上已经在 Looper 的构造方法中完成了,并且 MessageQueue 的构造方法只是对包内开放,所以我们也无法 new 一个 MessageQueue,所以对于一个线程有且仅有一个 MessageQueue;关于 Handler 的话 ——

《从源码角度简析 Android 消息机制》

其实也是一样的,一个线程有且仅有一个 Handler,不过我们也可以根据某个线程的 Looper 获取到该线程的 Handler

    原文作者:Android源码分析
    原文地址: https://juejin.im/entry/58cb8ffe570c350058966ce4
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞