Android | 源码阅读 — Android 的消息机制(一、初始化)

Android | 源码阅读 — Android 的消息机制(一、初始化),第1张

前言

打算开始系统地阅读 Android 源码,然后以博客的形式记录下来。

首先下手的目标是 Android 中的消息机制,涉及到的角色主要包括 Handler、Looper、Message 和 MessageQueue。首先选择消息机制的主要原因是不管在日常开发中,亦或是面试中,其出现的频率都很高,其次整个代码量适中,个人觉得比较适合新手入门。

网络上关于 Android 消息机制文章已经非常非常多了,包括使用啊、源码分析啊、各种详解啊等等。而且有很多很多都是讲的非常棒的,自己也从中学到了一些东西。但是每个人的切入点可能都不太一样,而我的这篇(包括后续的)主要是从我的视角出发,对 Android 消息机制的初始化流程进行分析、学习,然后做一个笔记,加深一下印象。

这可能会是一个小系列,这篇是这个小系列的第一篇。如标题所示,本篇会对消息机制的初始化过程中所涉及到的源码进行分析。包括初始化流程、Handler、Looper、MessageQueue 的对应关系等。

OK,开始吧~

正文

我们先回想一下,平时使用消息机制是怎么做的?或者说是怎么开始的?

99% 的情况是从创建一个 Handler 实例开始。于我个人而言,更多的情况是为 Activity 声明一个 Handler 的实例变量,在 Activity 初始化的时候对其初始化,大多数情况下是以匿名类的形式,如下:

mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

当然这是以前的做法,现在已经不倡导这么写了,Android 官网显示这个无参的构造函数已经被弃用了(下图),原因就是有内存泄漏的风险,非常非常不安全(虽然这么写仍然是可以实现线程间通信的)。

我们先看一下 Handler 的构造方法,有下面这么 7 个:

虽然总共有 7 个之多,但实际上最终得到调用的构造函数就两个:Handler(callback, async) 和 Handler(looper, callback, async),如下图:

我们先来看一下 Handler(callback, async) 的源码实现(注释全是我加的):


public Handler(Callback callback, boolean async) {

    // FIND_POTENTIAL_LEAKS:顾名思义,如果该值为 true,
    // 则会检测是否有内存泄漏de  风险
    if (FIND_POTENTIAL_LEAKS) {
    
        // 获取当前类类型
        final Class klass = getClass();
        
        // 如果当前 Handler 同时满足以下两个条件则存在内存泄漏的风险
        // 1. 该类是匿名类、或者内部类、或者局部类
        // 2. 该类不是静态类
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    // 获取当前线程的 Looper 对象
    mLooper = Looper.myLooper();  // 21行
    
    // 如果当前线程的 Looper 对象为 null,则抛出异常
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    
    // 获取当前线程对应 Looper 对象的 MessageQueue
    mQueue = mLooper.mQueue; // 31 行
    
    // 赋值 mCallback
    mCallback = callback;
    
    // 赋值 mAsynchronous 
    mAsynchronous = async;
}

针对上面的这段代码作以下说明:

  1. 参数列表:

    callback - 指定处理 Message 的回调,我们可以通过重写 Handler 的 handleMessage 方法处理消息,也可以通过指定回调处理。回调接口如下:

    public interface Callback {
        public boolean handleMessage(Message msg);
    }

    就一个 handleMessage 方法。

    async - 表示是否发送异步消息。一般我们用的时候默认情况比较多,也就是 async = false。关于异步消息、同步消息、同步屏障可以看这篇博客。感觉这是消息机制中比较容易被忽视的一个点。

  2. 关于 FIND_POTENTIAL_LEAKS :这个变量很奇怪,它的值在源码中固定为 false 的,并且没有任何地方可以对其值进行更改(也可能是我没找到?):

    所以我合理怀疑这个变量是 Google 的开发者们预留的调试代码

  3. 第 21 行:为当前 Handler 的 Looper 实例 mHandler 赋值,通过 Looper.myLooper() 获取当前线程(Handler 所在线程)的 Looper 实例, myLooper() 代码如下:

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

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

    当前线程的 Looper 实例是通过调用 Looper.prepare() 初始化的。这也是我们在非主线程使用 Handler 时必须首先调用 Looper.prepare() 的原因。

  4. 第 31 行:为当前 Handler 的 MessageQueue 实例 mQueue 赋值,这里获取的是 mLooper 中的 MessageQueue 对象,它是在 Looper 的构造方法中初始化的。下面的 quitAllowed 参数表示消息死循环是否可手动退出。

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

  5. 最后两行将参数列表中的 callback 和 async 分别赋值给 mCallback 和 mAsynchronous。

再看三个参数的 Handler(looper, callback, async) 的源码就简单多了:​​​​​​​

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

好清爽!就是一个一个的挨个赋值!非常简单!通过指定的 looper 创建 Handler 实例。

以上就是消息机制的初始化(或者说 Handler)过程。

总结
  1. 虽然 Handler 的构造方法有 7 个之多,但最终得到调用的就两个:Handler(callback, async) 和 Handler(looper, callback, async) 。

  2. Handler、Looper、MessageQueue 三者的引用关系是:Handler ← Looper ← MessageQueue,且 Looper 实例的初始化是在 Looper 类中,MessageQueue 的初始化是在 Looper 的构造方法中(prepare 方法)。

  3. 从 Handler 的构造函数我们可以看出线程与 Handler、Looper、MessageQueue 在数量上对应关系,即:一个线程可以有多个 Handler,但是只能有一个 Looper,进而只能有一个 MessageQueue。

 

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/867924.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存