类简介
默认情况下,线程没有消息循环;要创建一个消息循环,需要在要运行循环的线程中调用 Looper.prepare(),然后调用 Looper.loop() 来处理消息,直到循环被停止。而Looper 类就是用于运行一个消息循环的。
与消息循环的交互通常是通过 Handler 类来完成的。Handler 类允许你将消息和可运行对象(Runnable)发送到与一个线程的消息循环相关联的消息队列中。
该段文本是关于使用 Looper 线程实现的典型示例,通过分离 prepare 和 loop 来创建一个初始的 Handler 与 Looper 进行通信。
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler(Looper.myLooper()) {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
Looper 类包含了基于 MessageQueue 的事件循环设置和管理所需的代码。影响队列状态的 API 应该在 MessageQueue 或 Handler 上定义,而不是在 Looper 本身上定义。例如,空闲处理程序和同步屏障在队列上定义,而准备线程、循环和退出在 Looper 上定义。
换句话说,Looper 类主要负责管理事件循环的整体过程,包括准备线程、启动循环和退出循环等操作。而与具体的消息处理、延时任务等相关的操作则应该在 MessageQueue 或 Handler 类上定义,因为它们更直接地与队列和消息处理相关。
通过这种设计,可以将职责划分清楚,使代码更加模块化和可维护。Looper 类专注于事件循环的管理,而 MessageQueue 和 Handler 类则负责具体的消息处理和调度。
变量
//一个线程对应一个Looper对象,sThreadLocal 用来保存各个线程中的Looper对象。
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
//sMainLooper用来存储主线程的Looper对象
@UnsupportedAppUsage
private static Looper sMainLooper; // guarded by Looper.class
//sObserver是Message分发的观察者,在消息dispatch前后以及异常时会触发相关的回调。
private static Observer sObserver;
//mQueue是一个消息队列。一个线程对应一个Looper对象,一个Looper对象对应一个mQueue消息队列。
@UnsupportedAppUsage
final MessageQueue mQueue;
//mThread代表Looper对象所处的线程。
final Thread mThread;
//mInLoop用来记录是否已经执行了消息循环Looper.loop。
private boolean mInLoop;
//mLogging也是一个观察者,与observer不同的是,它是在消息的开始和消息的结束打印相关日志。
@UnsupportedAppUsage
private Printer mLogging;
//mTraceTag是Trace的TAG.
private long mTraceTag;
//消息dispatch的阈值
private long mSlowDispatchThresholdMs;
//dispatch - post称之为delivery,即delivery的阈值
private long mSlowDeliveryThresholdMs;
//是否delivery超过了阈值
private boolean mSlowDeliveryDetected;
方法
prepare()和prepare(boolean quitAllowed)
初始化当前线程的Looper对象,prepare参数为true
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(boolean quitAllowed)
- 创建MessageQueue对象,可以看到一个线程创建一个Looper,一个Looper创建一个MessageQueue,这里用mQueue表示。
- 获取当前线程的对象,赋值给mThread。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
prepareMainLooper()
- 调用prepare方法,参数为false
- 获取当前线程的Looper对象。该方法只会在主线程调用,所以该Looper对象为主线程的Looper对象,因此赋值给sMainLooper,表明自己是主线程的Looper对象
@Deprecated
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
getMainLooper
获取主线程的Looper
/**
* Returns the application's main looper, which lives in the main thread of the application.
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
setObserver
设置该进程中所有Looper的观察者。注意:是所有Looper,即设置所有线程的Looper的观察者。
/**
* Set the transaction observer for all Loopers in this process.
*
* @hide
*/
public static void setObserver(@Nullable Observer observer) {
sObserver = observer;
}
loopOnce
该方法代码比较多,分段分析。
该方法有三个参数,分别是调用方法执行所在线程的Looper对象,Binder.clearCallingIdentity()的返回值以及消息dispatch和delivery的阈值。
@SuppressWarnings("AndroidFrameworkBinderIdentity")
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride)
从Looper的MessageQueue中取出下一个要执行的Message,如果已经没有消息执行,直接返回false,并不会打印日志。
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
logging的观察者如果不为空,则输出当前消息的target、callback以及what值。
把成员变量赋值给局部变量,提高访问效率:将Observer的观察者对象赋值给observer
// 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;
首先获取Looper对象中的mTraceTag、mSlowDispatchThresholdMs以及mSlowDeliveryThresholdMs,然后检查thresholdOverride是否大于0,如果大于0,就用thresholdOverride的阈值替换Looper的mSlowDispatchThresholdMs和mSlowDeliveryThresholdMs的值。
如果slowDeliveryThresholdMs大于0并且msg.when > 0(msg还没有到dispatch分发的时间),那么logSlowDelivery就为true,如果slowDispatchThresholdMs大于0,logSlowDispatch就为true。
logSlowDelivery和logSlowDispatch有一个为true,就需要记录开始时间,如果logSlowDispatch为true,就需要记录结束时间。
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;
校验traceTag,并开始记录Trace
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
如果需要获取开始时间,就获取下目前的开机时间长。
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
- 所谓消息的分发,即dispatch,指的是调用消息的target Handler的dispatchMessage方法。
- 当消息的分发开始和结束以及异常时,执行observer的回调。
- dispatchMessage方法执行完,计算dispatchEnd。
- 如果dispatchMessage的过程中有异常,会先捕获,回调observer的异常,然后再throw。
- 结束Trace记录。
- origWorkSource 以后再说。
Object token = null;
Object msgMonitorToken = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
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);
}
}
logSlowDelivery意思是是否记录慢速投递,如果需要,那就在此校验是否已经发现了慢速投递的情况。mSlowDeliveryDetected一开始为默认值false,所以此时会执行else逻辑,接着执行showSlowLog方法,showSlowLog方法会去比较dispatchStart和msg.when的差值是否大于阈值slowDeliveryThresholdMs,如果大于,则输出相关msg信息到日志。同时设置me.mSlowDeliveryDetected = true,表示已经发现慢速投递的日志。下个消息处理的时候,如果dispatchStart - msg.when>10,那就不会再次打印日志信息,直到dispatchStart - msg.when) <= 10的时候,再会设置me.mSlowDeliveryDetected = false,这样下个消息处理的时候,又会再次校验是否输出日志。
if (logSlowDelivery) {
if (me.mSlowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
me.mSlowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
me.mSlowDeliveryDetected = true;
}
}
}
private static boolean showSlowLog(long threshold, long measureStart, long measureEnd,
String what, Message msg) {
final long actualTime = measureEnd - measureStart;
if (actualTime < threshold) {
return false;
}
// For slow delivery, the current message isn't really important, but log it anyway.
Slog.w(TAG, "Slow " + what + " took " + actualTime + "ms "
+ Thread.currentThread().getName() + " h="
+ msg.target.getClass().getName() + " c=" + msg.callback + " m=" + msg.what);
return true;
}
logSlowDispatch意思是是否记录慢速分发,如果需要,同样会调用showSlowLog输出相关日志。
logging回调如果不为空,则在消息处理结束后,回调相关信息。
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
这段代码用于确保在消息派发过程中,线程的身份(identity)没有被破坏或更改。
首先,通过调用 Binder.clearCallingIdentity() 方法获取当前线程的身份标识,并将其保存在 newIdent 变量中。
接下来,通过比较 ident 和 newIdent 的值来检查线程的身份是否发生了变化。ident 可能是之前保存的线程身份标识。
如果 ident 和 newIdent 的值不相等,则表示线程的身份发生了改变。这可能是一个异常情况,因为在消息派发过程中,线程的身份不应该发生变化。
在这种情况下,会输出一个严重级别的错误日志,使用 Log.wtf() 方法记录错误信息。错误信息包括线程身份标识从哪个值变为哪个值,以及正在派发消息的目标对象的类名、回调信息和消息的标识。
// 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分发成功后就进行回收,细节在Message.java源码分析时再说。
msg.recycleUnchecked();
loop
通过loop方法来运行当前线程的消息队列。通过quit方法退出loop,来结束对消息队列中消息的分发。
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
获取当前线程的Looper对象,标记Looper对象的mInLoop为true,表示已经执行了loop方法,如果再次调用loop方法,会有警告信息:即再次进入循环将导致在当前消息完成之前执行排队的消息。
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
希望以本地进程的身份来调用loopOnce方法,并且设计期望方法调用过程中,身份保持不变.
可以使用设置系统属性的方式来修改阈值.
开启无限循环调用loopOnce方法.当loopOnce返回false的时候,停止循环.
// 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);
me.mSlowDeliveryDetected = false;
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
myLooper
返回当前线程关联的Looper对象
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
myQueue
返回当前线程关联的消息队列对象
/**
* Return the {@link MessageQueue} object associated with the current
* thread. This must be called from a thread running a Looper, or a
* NullPointerException will be thrown.
*/
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
isCurrentThread
返回当前线程是否与Looper关联的线程是一个线程
/**
* Returns true if the current thread is this looper's thread.
*/
public boolean isCurrentThread() {
return Thread.currentThread() == mThread;
}
setMessageLogging
设置Printer回调
/**
* Control logging of messages as they are processed by this Looper. If
* enabled, a log message will be written to <var>printer</var>
* at the beginning and ending of each message dispatch, identifying the
* target Handler and message contents.
*
* @param printer A Printer object that will receive log messages, or
* null to disable message logging.
*/
public void setMessageLogging(@Nullable Printer printer) {
mLogging = printer;
}
setTraceTag
设置TraceTag
/** {@hide} */
@UnsupportedAppUsage
public void setTraceTag(long traceTag) {
mTraceTag = traceTag;
}
setSlowLogThresholdMs
设置消息被分发dispatch(消息被处理的时间)的阈值
设置消息被递送delivered(从send消息到dispatch开始的时间)的阈值
/**
* Set a thresholds for slow dispatch/delivery log.
* {@hide}
*/
public void setSlowLogThresholdMs(long slowDispatchThresholdMs, long slowDeliveryThresholdMs) {
mSlowDispatchThresholdMs = slowDispatchThresholdMs;
mSlowDeliveryThresholdMs = slowDeliveryThresholdMs;
}
quit
退出loop,即不再循环处理消息队列中的消息.
此时调用sendMessage,会返回false.表示send失败
此方法不是安全的,因为在loop退出之前一些信息可能没有被递送delivered,即如果消息队列中有消息,则不会再继续处理.
/**
* Quits the looper.
* <p>
* Causes the {@link #loop} method to terminate without processing any
* more messages in the message queue.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p><p class="note">
* Using this method may be unsafe because some messages may not be delivered
* before the looper terminates. Consider using {@link #quitSafely} instead to ensure
* that all pending work is completed in an orderly manner.
* </p>
*
* @see #quitSafely
*/
public void quit() {
mQueue.quit(false);
}
quitSafely
此时调用sendMessage,会返回false.表示send失败
处理已到期的消息:所有已经到达处理时机的消息,将在 loop 终止前被处理完毕。
忽略未来的延迟消息:那些设定了未来处理时间的延迟消息不会被处理
/**
* Quits the looper safely.
* <p>
* Causes the {@link #loop} method to terminate as soon as all remaining messages
* in the message queue that are already due to be delivered have been handled.
* However pending delayed messages with due times in the future will not be
* delivered before the loop terminates.
* </p><p>
* Any attempt to post messages to the queue after the looper is asked to quit will fail.
* For example, the {@link Handler#sendMessage(Message)} method will return false.
* </p>
*/
public void quitSafely() {
mQueue.quit(true);
}
getThread
获取与Looper关联的线程
/**
* Gets the Thread associated with this Looper.
*
* @return The looper's thread.
*/
public @NonNull Thread getThread() {
return mThread;
}
getQueue
获取当前Looper关联的消息队列
/**
* Gets this looper's message queue.
*
* @return The looper's message queue.
*/
public @NonNull MessageQueue getQueue() {
return mQueue;
}
dump
将looper中的状态dump到Printer的println方法中,以及将消息队列mQueue中的内容也回调到Printer的println方法中
mQueue的dump方法的第三个参数为null,表示会dump所有关联的Handler的消息,不为null,就表示只dump与该Handler相关的消息.具体的会在MessageQueue源码分析中讲解.
/**
* Dumps the state of the looper for debugging purposes.
*
* @param pw A printer to receive the contents of the dump.
* @param prefix A prefix to prepend to each line which is printed.
*/
public void dump(@NonNull Printer pw, @NonNull String prefix) {
pw.println(prefix + toString());
mQueue.dump(pw, prefix + " ", null);
}
/**
* Dumps the state of the looper for debugging purposes.
*
* @param pw A printer to receive the contents of the dump.
* @param prefix A prefix to prepend to each line which is printed.
* @param handler Only dump messages for this Handler.
* @hide
*/
public void dump(@NonNull Printer pw, @NonNull String prefix, Handler handler) {
pw.println(prefix + toString());
mQueue.dump(pw, prefix + " ", handler);
}
toString
输出线程名称,线程ID,Looper对象的hashcode.
@Override
public String toString() {
return "Looper (" + mThread.getName() + ", tid " + mThread.getId()
+ ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";
}
Observer
定义了Observer接口类,回调方法分别代表消息分发开始,消息分发结束,消息分发异常.
/** {@hide} */
public interface Observer {
/**
* Called right before a message is dispatched.
*
* <p> The token type is not specified to allow the implementation to specify its own type.
*
* @return a token used for collecting telemetry when dispatching a single message.
* The token token must be passed back exactly once to either
* {@link Observer#messageDispatched} or {@link Observer#dispatchingThrewException}
* and must not be reused again.
*
*/
Object messageDispatchStarting();
/**
* Called when a message was processed by a Handler.
*
* @param token Token obtained by previously calling
* {@link Observer#messageDispatchStarting} on the same Observer instance.
* @param msg The message that was dispatched.
*/
void messageDispatched(Object token, Message msg);
/**
* Called when an exception was thrown while processing a message.
*
* @param token Token obtained by previously calling
* {@link Observer#messageDispatchStarting} on the same Observer instance.
* @param msg The message that was dispatched and caused an exception.
* @param exception The exception that was thrown.
*/
void dispatchingThrewException(Object token, Message msg, Exception exception);
}
dumpDebug
将线程名称,线程ID,以及消息队列中的消息写到ProtoOutputStream中
/** @hide */
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long looperToken = proto.start(fieldId);
proto.write(LooperProto.THREAD_NAME, mThread.getName());
proto.write(LooperProto.THREAD_ID, mThread.getId());
if (mQueue != null) {
mQueue.dumpDebug(proto, LooperProto.QUEUE);
}
proto.end(looperToken);
}
Looper和MessageQueue的关系
当Looper创建的时候,就会创建一个MessageQueue,所以Looper和MessageQueue是一一对应的关系。