打算开始系统地阅读 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 extends Handler> 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;
}
针对上面的这段代码作以下说明:
-
参数列表:
callback - 指定处理 Message 的回调,我们可以通过重写 Handler 的 handleMessage 方法处理消息,也可以通过指定回调处理。回调接口如下:
public interface Callback { public boolean handleMessage(Message msg); }
就一个 handleMessage 方法。
async - 表示是否发送异步消息。一般我们用的时候默认情况比较多,也就是 async = false。关于异步消息、同步消息、同步屏障可以看这篇博客。感觉这是消息机制中比较容易被忽视的一个点。
-
关于 FIND_POTENTIAL_LEAKS :这个变量很奇怪,它的值在源码中固定为 false 的,并且没有任何地方可以对其值进行更改(也可能是我没找到?):
所以我合理怀疑这个变量是 Google 的开发者们预留的调试代码
-
第 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() 的原因。
-
第 31 行:为当前 Handler 的 MessageQueue 实例 mQueue 赋值,这里获取的是 mLooper 中的 MessageQueue 对象,它是在 Looper 的构造方法中初始化的。下面的 quitAllowed 参数表示消息死循环是否可手动退出。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
-
最后两行将参数列表中的 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)过程。
总结-
虽然 Handler 的构造方法有 7 个之多,但最终得到调用的就两个:Handler(callback, async) 和 Handler(looper, callback, async) 。
-
Handler、Looper、MessageQueue 三者的引用关系是:Handler ← Looper ← MessageQueue,且 Looper 实例的初始化是在 Looper 类中,MessageQueue 的初始化是在 Looper 的构造方法中(prepare 方法)。
-
从 Handler 的构造函数我们可以看出线程与 Handler、Looper、MessageQueue 在数量上对应关系,即:一个线程可以有多个 Handler,但是只能有一个 Looper,进而只能有一个 MessageQueue。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)