一. 简介
我们接上一篇文章:Android Handler机制(二) Handler 实现原理 继续分析Looper
Looper 的职责很单一,就是单纯的从 MessageQueue 中取出消息分发给消息对应 的宿主 Handler,因此它的代码不多(400行左右) . Looper 是线程独立的且每个线程只能存在一个 Looper。 Looper 会根据自己的存活情况来创建和退出属于它自己的 MessageQueue。
二. 创建和退出Looper
2.1 创建Looper
上面的结论中提到了 Looper 是线程独立的且每个线程只能存在一个 Looper。所以构造 Looper 实例的方法类似于单例模式。隐藏构造方法,对外提供了两个指定 的获取实例方法 prepare()和 prepareMainLooper()。
源码解析:
// 应用主线程(UI 线程)Looper 实例
private static Looper sMainLooper;
// Worker 线程 Looper 实例,用 ThreadLocal 保存的对象都是线程独立的
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// 与当前 Looper 对应的消息队列
final MessageQueue mQueue;
// 当前 Looper 所在的线程
final Thread mThread;
//对外公开初始化方法 在普通线程中初始化 Looper 调用此方法
public static void prepare() {
// 初始化一个可以退出的 Looper ,注意这里的传递的参数为true 表示可以退出
prepare(true);
}
//对外公开初始化方法, 在应用主线程(UI 线程)中初始化 Looper 调用此方法
public static void prepareMainLooper() {
//因为是主线程,初始化一个不允许退出的 Looper
prepare(false);
synchronized (Looper.class) {
// 如果 sMainLooper 不等于空说明已经创建过主线程 Looper 了,不 应该重复创建
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//内部私有初始化方法 @param quitAllowed 是否允许退出 Looper
private static void prepare(boolean quitAllowed) {
// 每个线程只能有一个 Looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 保存实例 保证每个Looper对线程一一对应.
sThreadLocal.set(new Looper(quitAllowed));
}
//私有构造方法 @param quitAllowed 是否允许退出 Looper
private Looper(boolean quitAllowed) {
// 初始化 MessageQueue
mQueue = new MessageQueue(quitAllowed);
// 得到当前线程实例
mThread = Thread.currentThread();
}
从源码分析可以知道:
真正创建 Looper 实例的构造方法中其实很简单,就是创建了对应的 MessageQueue 实例,然后得到当前线程,值得注意的是 MessageQueue 和线程 实例都是被 final 关键字修饰的,只能被赋值一次。
对外公开初始化方法 prepareMainLooper()是为应用主线程(UI 线程)准备的, 应用刚被创建就会调用该方法,所以我们不该再去调用它。
开发者可以通过调用对外公开初始化方法 prepare()对自己的 worker 线程创建 Looper,但是要注意只能初始化一次。
调用 Looper.prepare()方法初始化完成后,可以调用 myLooper()和 myQueue() 方法得到当前线程对应的实例:
/**
* Returns the application's main looper, which lives in the main thread of the application.
获取UI线程的Looper
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
/**
* Gets this looper's message queue.
获取Looper一一对应的 MessageQueue
*
* @return The looper's message queue.
*/
public @NonNull MessageQueue getQueue() {
return mQueue;
}
/**
* Gets the Thread associated with this Looper.
获取Looper依附于的线程
*
* @return The looper's thread.
*/
public @NonNull Thread getThread() {
return mThread;
}
2.2 退出Looper
退出 Looper 有安全与不安全两种退出方法,其实对应的就是 MessageQueue 的 安全与不安全方法:
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
两个方法的区别
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
//安全退出
removeAllFutureMessagesLocked();
} else {
//直接退出
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
//从队列头中取消息,有一个算 一个,全部拿出来回收掉
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
private void removeAllFutureMessagesLocked() {
//System.currentTimeMillis() 代表的是从 1970-01-01 00:00:00 到当前时间的毫秒数
//SystemClock.uptimeMillis()获取当前系统时间:从系统开机到现在的毫秒数(不包括深度休眠的时间)
//假设当前时间为 8:00:00
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
// 判断当前消息对象的预处理时间是否晚于当前时间
if (p.when > now) {
// 如果当前消息对象的预处理时间晚于当前时间直接全部暴力清除
removeAllMessagesLocked();
} else {
Message n;
// 如果当前消息对象的预处理时间并不晚于当前时间
// 说明有可能这个消息正在被分发处理
// 那么就跳过这个消息往后找晚于当前时间的消息
for (;;) {
n = p.next;
//消息链表最后一个为null,说明没有消息了,就直接return掉
if (n == null) {
return;
}
// 如果找到了晚于当前时间的消息结束循环
// 消息队列已经是通过时间先后排列好的, 当你找到了第一个 8:00:05的消息, 那么链表后面的肯定都是大于8:00:05的消息,所以这里就可以直接break退出循环了.
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
// n 就是那个晚于当前时间的消息
// 从 n 开始之后的消息全部回收
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
removeAllMessagesLocked(),简单暴力直接清除掉队列中所有的消息。
removeAllFutureMessagesLocked(),清除掉可能还没有被处理的消息(就是说正在处理的消息不处理, 剩下的全部清除回收)
这么看来这个方法名字起的还挺合理的,很好的解释了是要删除还没有被处理的消息。
三. 运行 Looper 处理消息
调用 Looper.prepare()方法初始化完成 Looper 后就可以让 Looper 去工作了,只需要调用 Looper.loop()方法即可。
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
// 得到当前线程下的 Looper
final Looper me = myLooper();
// 如果还没初始化过抛异常 Looper.prepare() 还没有被调用
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// 得到当前线程下与 Looper 对应的消息队列
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 {
//尝试将消息分发给宿主(Handler)
//dispatchMessage 为宿主 Handler 的接收消息方法
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();
}
}
四. Looper必须掌握的知识点
1. Question: 一个线程有几个Looper?
Answer: 一个
2. Question :子线程中创建Handler 需要做什么?
Answer: 三部曲
1. Looper.prepare
|
|
//这里是工作区
|
|
2. Looper.loop
3. Looper.quit
3. Question: 子线程工作完后,需要退出(Looper.quit) ,那么UI线程需要退出嘛?
Answer: 不需要, 主线程不让退出
五. 总结
Looper 的功能很简单,核心方法 Looper.loop()就是不断的从消息队列中取出消 息分发给对应的宿主 Handler,它与对应 MessageQueue 息息相关,一起创建, 一起退出。
Looper 更想强调的是线程的独立性与唯一性,利用 ThreadLocal 保证每个线程只 有一个 Looper 实例的存在。利用静态构造实例方法保证不能重复创建 Looper。
Looper.prepareMainLooper()是比较特殊的方法,它是给 UI 线程准备,理论上 开发者在任何情况下都不应该调用它。
六. 致谢
感谢享学的老师,让人理解了这些晦涩难懂的源码, 也加入了自己的一些理解. 供大家参考,谢谢