成员变量
// Log的TAG
private static final String TAG = "Looper";
// 线程本地变量,保证了每个线程仅有唯一的Looper对象。
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
// 主线程的Looper,由ActivityThread的main方法内调用Looper.prepareMainLooper()进行创建。
@UnsupportedAppUsage
private static Looper sMainLooper;
// 消息分发状态的观察者,有分发开始、分发异常、分发结束。
private static Observer sObserver;
// 消息队列
@UnsupportedAppUsage
final MessageQueue mQueue;
// 创建Looper的线程
final Thread mThread;
// 是否在loop中
private boolean mInLoop;
// 日志打印类
@UnsupportedAppUsage
private Printer mLogging;
// Trace追踪标记
private long mTraceTag;
// 慢分发阈值时间,如果设置了这个参数,如果消息分发的时间超过这个值,则这个looper将显示一个警告日志。
private long mSlowDispatchThresholdMs;
// 慢交付阈值时间,如果设置了这个参数,如果消息执行(实际交付时刻dispatchStart - post时刻msg.when)的时间超过这个值,则这个looper将显示一个警告日志。
private long mSlowDeliveryThresholdMs;
// 是否慢交付检测,如果消息交付时间超过mSlowDeliveryThresholdMs,则为true。
private boolean mSlowDeliveryDetected;
复制代码
说明:
Looper
为什么需要持有
MessageQueue
,因为
Looper
轮询
的时候需要知道轮询哪个
MessageQueue
。
创建Looper
想要
使用
Looper
,首先要
创建
Looper
,所以我们接下来看下它是如何被
创建
的。
Looper.prepare()
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
// 调用线程已经有Looper了,再进行prepare,则抛出异常(每个线程只能创建一个Looper)。
throw new RuntimeException("Only one Looper may be created per thread");
}
// 创建Looper,并存入到sThreadLocal中。
sThreadLocal.set(new Looper(quitAllowed));
}
复制代码
prepare()
方法,为
静态
方法,为
调用线程
创建一个
Looper
并
存入
到
sThreadLocal
中,以便后续
获取此线程
的
Looper
。
quitAllowed
参数传入为
ture
,以
允许退出
Looper
。
说明:
一个线程
,只能
调用一次
prepare()
、
prepare(boolean)
方法,否则
抛出异常
。
一个线程
,只能
有一个
Looper
。
prepare()
方法创建的
Looper
,
允许退出
。
接下来我们来看一下
Looper
构造方法
。
private Looper(boolean quitAllowed) {
// 创建消息队列
mQueue = new MessageQueue(quitAllowed);
// 记录创建线程
mThread = Thread.currentThread();
}
复制代码
Looper
构造方法
,为
私有
方法,
不能
通过
new
创建,参数
quitAllowed
为
是否允许退出
,创建
Looper
会创建一个
MessageQueue
,并记录
创建线程
。
说明:
一个
Looper
,
只有一个
MessageQueue
,
只有一个关联
Thread
。
Looper.prepareMainLooper()
@Deprecated
public static void prepareMainLooper() {
// 准备Looper,如没有则进行创建,不允许退出。
prepare(false);
// 同步,保证线程安全。
synchronized (Looper.class) {
if (sMainLooper != null) {
// 已经创建MainLooper了,再准备,则抛出异常。
throw new IllegalStateException("The main Looper has already been prepared.");
}
// 获取调用线程的Looper,为MainLooper。
sMainLooper = myLooper();
}
}
复制代码
prepareMainLooper()
方法,为
静态
方法,为
主线程
准备
Looper
,并传入
quitAllowed
为
false
使其
不允许退出
,并将其
标记
为
app
的
主
looper
。
myLooper()
相关介绍,看后面的-
获取Looper
。
已被声明
为
@Deprecated
(弃用),因为
app
的
主线程
looper
是由
Android
环境
创建
的(
ActivityThread
的
main
方法内调用
Looper.prepareMainLooper()
创建),因此永远
不需要
自己
调用
这个函数。
说明:
多个线程
只能
调用一次
prepareMainLooper()
方法,否则抛出异常。
主线程
Looper
,是由
Android
环境
创建
的(
ActivityThread
的
main
方法),不需要我们关心。
主线程
Looper
,
不允许退出
。
小结
创建
–
非主线程
Looper
,
只能
通过
Looper.prepare()
方法
创建
,它是
允许退出
的。
创建
–
主线程
Looper
,它是通过
Android
环境
创建
(
ActivityThread
的
main
方法内调用
Looper.prepareMainLooper()
创建),它是
不允许退出
的。- 一个
线程
,一个
Looper
,一个
MessageQueue
。
获取Looper
创建完
Looper
后,便可以
获取
Looper
使用
了,所以我们接下来看下它是如何被
创建
的。
Looper.myLooper()
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
复制代码
myLooper()
方法,为
静态
方法,返回与
当前线程
关联的
Looper
对象。如果
调用线程没有
与
Looper
关联,则返回
null
。
Looper.getMainLooper()
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
复制代码
getMainLooper()
方法,为
静态
方法,返回
主线程
Looper
对象。
小结
获取
Looper
,有两个方法:
Looper.myLooper()
、
Looper.getMainLooper()
。
Looper.myLooper()
方法,获取
调用线程
的
Looper
,有可能为
null
。
Looper.getMainLooper()
方法,获取
主线程
的
Looper
,不会为
null
。
轮询Looper
当
Looper
创建好后,需要调用
Looper.loop()
方法,使其进入
消息循环
中,所以我们接下来看下它的
Looper.loop()
方法。
Looper.loop()
public static void loop() {
// 获取当前线程Looper对象
final Looper me = myLooper();
if (me == null) {
// Looper对象为空,抛出异常(没有Looper; 这个线程上没有调用Looper.prepare())。
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
// 已经在loop中,再调用loop(),则提示警告(再次loop将使队列中的消息在此消息完成之前被执行)。
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
// 标记在loop中
me.mInLoop = true;
// 确保这个线程的标识是本地进程的标识,并跟踪标识token实际上是什么。
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// 允许用系统道具(adb命令)覆盖一个阈值,例如:
// 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);
// 慢交付检测,恢复默认值。
me.mSlowDeliveryDetected = false;
// 死循环,遍历消息队列里的消息。
for (;;) {
// 调用loopOnce方法,进行处理。
if (!loopOnce(me, ident, thresholdOverride)) {
// loopOnce方法返回false,退出循环,结束loop。
return;
}
}
}
复制代码
loop()
方法,为
静态
方法,内部执行
死循环
进行
轮询
,调用
loopOnce()
方法
轮询单个消息
。
loopOnce()
方法返回
true
,则继续
轮询下一个消息
;
返回
false
,则
退出循环
,
结束
loop
。
说明:
当前线程
调用
Looper.loop()
方法前,
一定
要为
当前线程
准备
Looper
(调用
Looper.prepare()
),否则
抛出异常
。
接下来,我们来看一下
loopOnce()
方法。
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
// 获取此Looper的消息队列里面的下个消息,有可能会阻塞线程。
Message msg = me.mQueue.next();
if (msg == null) {
// 没有消息,表示消息队列正在退出(调用quit()方法),直接返回false以退出loop。
return false;
}
// 获取日志打印类,这必须在一个局部变量中,以防UI事件设置logger。
final Printer logging = me.mLogging;
if (logging != null) {
// 打印日志:Dispatching(分发中)
logging.println(">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what);
}
// 获取Looper的全局观察者,使用变量,确保观察者在处理事务时不会更改。
final Observer observer = sObserver;
// 获取追踪标记
final long traceTag = me.mTraceTag;
// 获取消息分发阈值
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
// 获取消息交付阈值
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
// 有覆盖的(adb命令传入),则用覆盖的值。
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;
// token,用于Observer回调间传递。
Object token = null;
if (observer != null) {
// 获取token,通知Observer消息分发开始。
token = observer.messageDispatchStarting();
}
// 设置当前线程的Uid
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
// 通知Message的Handler进行分发消息(重要!!!)
msg.target.dispatchMessage(msg);
if (observer != null) {
// 通知Observer消息分发结束
observer.messageDispatched(token, msg);
}
// 记录分发结束时刻
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
// 通知Observer消息分发异常
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
// 恢复当前线程的Uid
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
// 结束追踪
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
// 记录慢交付,如果(dispatchStart - msg.when)时间大于阈值,则进行Slog提示警告。
if (me.mSlowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
me.mSlowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
me.mSlowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
// 记录慢分发,如果(dispatchEnd - dispatchStart)时间大于阈值,则进行Slog提示警告。
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
// 打印日志:Finished(已完成)
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// 确保在分发过程中线程的标识没有损坏。
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();
return true;
}
复制代码
loopOnce()
方法,为
静态
方法,为
轮询单个消息
。它会先获取
消息队列
的
下个
Message
,然后调用
Message
的
目标
Handler
进行
分发
。
说明:
- 当
Looper
的
MessageQueue
里面
没有
消息时,则线程
进入阻塞
,释放
CPU
,它
不会因为没有数据而停止
loop()
,所以如果此
Looper
不使用了
,
一定要
调用
quit()
来
结束循环
,它会使
MessageQueue
的
next()
方法返回
null
,从而
结束循环
。- 可通过调用
looper.setMessageLogging()
给
指定
Looper
设置日志打印者,以
监听消息状态
,
>>>>> Dispatching to
为
分发开始
,
<<<<< Finished to
为
分发结束
,可判断
时间差
来判断
分发是否超时
。
小结
轮询
Looper
前,
调用
线程
一定
要有
Looper
(需调用
Looper.prepare()
),否则
抛出异常
。
非主线程
Looper
,如果不使用了,
一定要
调用
quit()
来
结束循环
。- 当
MessageQueue
里面
没有
消息时,则线程
进入阻塞
,释放
CPU
,它
不会因为没有数据而停止
loop()
,所以如果此
Looper
不使用了
,
一定要
调用
quit()
来
结束循环
。
退出Looper
Looper
不使用了
,便需要
退出
,所以我们接下来看下它是如何
退出
的。
quit()
public void quit() {
mQueue.quit(false);
}
复制代码
quit()
方法,为
不安全
的
退出
Looper
。
说明:
- 此方法,导致
loop()
方法,在
不处理
消息队列中的
任何消息
的情况下
终止
,因为它会
删除消息队列
中
所有消息
。- 在
looper
被请求
退出
后,
任何
向队列
发送消息
的尝试
都将失败
。例如,
Handler.sendMessage(Message)
方法将返回
false
。- 此方法,
可能
是
不安全
的,因为在
looper
终止之前
可能
无法传递
一些消息。考虑使用
quitSafely
,以
确保所有等待的工作
都以有序的方式
完成
。
quitSafely()
public void quitSafely() {
mQueue.quit(true);
}
复制代码
quitSafely()
方法,为
安全
的
退出
Looper
。
说明:
- 此方法,导致
loop()
方法,在
处理
消息队列中的
已经到期
的
所有消息
,
不处理
消息队列中的
未来(当前时刻以后)到期
的
所有消息
的情况下
终止
,因为它会
删除消息队列
中
所有未来的消息
。
小结
退出
Looper
,有两个方法:
quit()
不安全退出、
quitSafely()
安全退出。
quit()
方法,在
不处理
消息队列中的
任何消息
的情况下
终止
(此方法,
可能
是
不安全
的,因为在
looper
终止之前
可能
无法传递
一些消息)。
quitSafely()
方法,在
处理
消息队列中的
已经到期
的
所有消息
,
不处理
消息队列中的
未来(当前时刻以后)到期
的
所有消息
的情况下
终止
。- 在
looper
被请求
退出
后,
任何
向队列
发送消息
的尝试
都将失败
。例如,
Handler.sendMessage(Message)
方法将返回
false
。
属性set、get方法
Looper
的
mLogging
、
mThread
、
mQueue
属性对外提供了
set
、
get
方法,我们接下来看下它们的实现。
setMessageLogging()
public void setMessageLogging(@Nullable Printer printer) {
mLogging = printer;
}
复制代码
设置
此
Looper
处理消息
时
控制消息
的
日志记录
。如果
启用
,
日志消息
将在
每次
消息
分发
的
开始
和
结束
时写到
printer
,
标识
目标
Handler
和消息内容。
getThread()
public @NonNull Thread getThread() {
return mThread;
}
复制代码
获取
与此
looper
关联
的
线程
。
getQueue()
public @NonNull MessageQueue getQueue() {
return mQueue;
}
复制代码
获取
此
looper
的
消息队列
。
其它
Looper.myQueue()
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
复制代码
获取
与
当前线程
关联的
MessageQueue
对象。此线程
必须
有
Looper
,
否则
将
抛出
NullPointerException
。
isCurrentThread()
public boolean isCurrentThread() {
return Thread.currentThread() == mThread;
}
复制代码
判断
调用线程
是否是该
looper
的
线程
,如果是,则返回
true
。
dump()
public void dump(@NonNull Printer pw, @NonNull String prefix) {
pw.println(prefix + toString());
mQueue.dump(pw, prefix + " ", null);
}
复制代码
转储
looper
的
状态
,以进行
调试
,调用
MessageQueue
的
dump()
方法。