目录
- 前言
- 01、 用法
- 02、源码
- 03、结语
前言
在【Android】线程间通信 - Handler之使用篇主要讲了 Handler 的创建,发送消息,处理消息 三个步骤。那么接下来,我们也按照这三个步骤,从源码中去探析一下它们具体是如何实现的。本篇是关于创建源码的分析。
01、 用法
先回顾一下,在主线程和非主线程是如何创建 Handler 的。
//主线程 | |
private val mHandler: Handler = object : Handler(Looper.getMainLooper()) { | |
override fun handleMessage(msg: Message) { | |
when (msg.what) { | |
1 -> {} | |
} | |
} | |
} | |
//子线程 | |
Thread { | |
Looper.prepare() | |
val handler = object : Handler() { | |
override fun handleMessage(msg: Message) { | |
when (msg.what) { | |
1 -> {} | |
} | |
} | |
} | |
handler.sendEmptyMessage(1) | |
Looper.loop() | |
}.start() |
02、源码
Handler 一共有 7 个构造方法。但最后都会直接或间接使用到以下两个构造方法。所以我们看看两个方法都做了什么事情吧。
//方法 1 | |
//Handler.java | |
public Handler( Callback callback, boolean async) { | |
if (FIND_POTENTIAL_LEAKS) { | |
final Class<? extends Handler> klass = getClass(); | |
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()); | |
} | |
} | |
mLooper = Looper.myLooper(); \\标识 1 | |
if (mLooper == null) { \\ 标识 3 | |
throw new RuntimeException( | |
"Can't create handler inside thread " + Thread.currentThread() | |
+ " that has not called Looper.prepare()"); | |
} | |
mQueue = mLooper.mQueue; \\标识 2 | |
mCallback = callback; | |
mAsynchronous = async; | |
} | |
//方法 2 | |
public Handler( Looper looper, Callback callback, boolean async) { | |
mLooper = looper; | |
mQueue = looper.mQueue; | |
mCallback = callback; | |
mAsynchronous = async; | |
} |
方法 2 更加的简单,那么我们就从它入手吧。在其中对 4 个变量进行赋值。分别是 mLooper, mQueue, mCallback, mAsynchronous
。方法 1 主要也是对 4 个变量进行赋值。它们有什么作用,我们先不管,后面会讲到。我们先来看看这方法 1 和方法 2 的区别是什么?
让我们聚焦到标识 1 和标识 2,mLooper, mQueue 来源是通过 Looper 这个类中获取的。那让我们跟进去看看。
//跟进标识 1 | |
//Looper.java | |
public static @Nullable Looper myLooper() { | |
return sThreadLocal.get(); | |
} |
看到是通过 mThreadLocal.get()
获得一个 Looper 实例。那么 mThreadLocal
的 Looper 又是哪里来的呢?让我们找找 mThreadLocal.set()
方法,就知道了!
//Looper.java | |
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.prepare()
中添加的。
这里需要注意!
- "Only one Looper may be created per thread",每个线程只能调用一次
prepare()
,这也就意味着每个线程只有一个 Looper 。
可是看回在主线程中创建 Handler 的时候,我们并没有调用 Looper.prepare()
方法。但是也正常运行了。那是为什么呢?那是因为在 ActivityThread 中的 main()
已经调用了。
//ActivityThread.java | |
public static void main(String[] args) { | |
//...省略无关代码 | |
Looper.prepareMainLooper(); \\标识 4 | |
//... | |
ActivityThread thread = new ActivityThread(); | |
thread.attach(false, startSeq); | |
if (sMainThreadHandler == null) { | |
sMainThreadHandler = thread.getHandler(); | |
} | |
//... | |
Looper.loop(); | |
} | |
//跟进标识 4 | |
//Looper.java | |
public static void prepareMainLooper() { | |
prepare(false); | |
synchronized (Looper.class) { | |
if (sMainLooper != null) { | |
throw new IllegalStateException("The main Looper has already been prepared."); | |
} | |
sMainLooper = myLooper(); | |
} | |
} |
这里需要注意!
- 我们不仅不用调用,也不能调用。否则将会触发
IllegalStateException("The main Looper has already been prepared.")
异常。 - 但是如果不是为主线程创建 Handler 的时候,我们就需要手动调用
Looper.prepare()
, 否则该线程中的 Looper 为空,会触发标识 3 的代码块。 - 即会得到
RuntimeException("Can't create handler inside thread " + Thread.currentThread()+ " that has not called Looper.prepare()")
异常。
03、结语
图源 |《第一行代码》
最后结合《第一行代码》中异步消息处理机制的流程图。我们可以看出 Handler 创建过程主要是准备好了 Looper, MessageQueue 和 Handler 本身。