0. 系列文章汇总
-
Android Handler消息机制01-Message源码学习
-
Android Handler消息机制02-Looper源码学习
-
Android Handler消息机制03-Message源码学习
1.源码
本文主要是对Looper类的源码进行解析学习,用于更深入的理解Handler消息机制
Looper的源码路径为:
android.os.Looper
1.2 典型案例
如下展示了一个源码中提供的典型实例
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
如上所示的是在一个子线程构建一个Handler,但是在实际开发中不建议这么做。一般来说Handler机制是用于子线程往主线程传递消息用于UI更新操作。如果实在要在子线程创建,请使用
HandlerThread
进行创建
/**
* Looper类被用于运行一个线程的消息循环,消息默认没有与之关联的消息循环,为了创建一个,在运行循环的线程中调用Looper.prepare,
* 然后调用loop来处理消息直到循环停止。
*
* <p>与消息循环交互的大多数交互是通过Handler类。
*/
public final class Looper {
/*
* API Implementation Note:
*
* 该类基于MessageQueue去设置和管理事件循环,影响队列状态的API应在MessageQueue或者Handler上定义,而不是在Looper本身上定义,
* 例如默认Handlers和同步屏障在队列中定义,而prepare、loop和quit定义在Looper中
*/
private static final String TAG = "Looper";
// sThreadLocal 会返回null 直到你调用prepare,表示当前线程的Looper对象
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
@UnsupportedAppUsage
private static Looper sMainLooper; // 静态关联主线程的Looper
private static Observer sObserver;
@UnsupportedAppUsage
final MessageQueue mQueue;
final Thread mThread;
//
private boolean mInLoop;
@UnsupportedAppUsage
private Printer mLogging;
private long mTraceTag;
/**
* If set, the looper will show a warning log if a message dispatch takes longer than this.
* 如果赋值,Looper会打印一个告警日志如果消息处理耗时比这个值大。
*/
private long mSlowDispatchThresholdMs;
/**
* If set, the looper will show a warning log if a message delivery (actual delivery time -
* post time) takes longer than this.
* 如果被赋值,且消息分发(实际 delivery time -post time)耗时过长且会打印一个告警日志
*/
private long mSlowDeliveryThresholdMs;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread(); //mThread置为当前线程
}
/**
* 初始化当前线程为一个Looper
* 这提供了在实际开始loop之前,创建关联当前Looperr的Handler的时机,
* 确保在调用此方法后再调用#loop(),然后通过#quit()来结束他
*
*/
public static void prepare() {
prepare(true);
}
/**
* 准备此Looper
* @param quitAllowed 是否允许此Hnadler停止
*/
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,是在ActivityThread调用。
*
* @deprecated 进程的主Looper是通过Android系统创建的,所以永远不需要自己调用prepareMainLooper方法。
*/
@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();
}
}
/**
* 返回当前的主Looper,关联着当前应用的主线程。
*/
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
/**
* 设置当前进程的所有Looper设置观察者
*
* @hide
*/
public static void setObserver(@Nullable Observer observer) {
sObserver = observer;
}
/**
* 运行当前线程的消息队列,确保call #quit() 在循环结束后
*/
public static void 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;
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();
// 允许使用系统参数覆盖阈值,供系统使用
// 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(); // 可能会阻塞
if (msg == null) {
// 没有消息则此消息队列会退出处理流程
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);
}
// 确保观察者在处理事务时不会改变的.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs; // 消息处理耗时阈值
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs; // 消息分发耗时阈值
if (thresholdOverride > 0) { // 如果从系统变量里面读取的值不等于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); // 可选字段,指示导致该消息进入队列的uid,仅用于系统服务
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);
}
}
// 关于分发的耗时操作日志处理,删除
// 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);
}
// 回收Message对象,注意dispatchMessage与recycleUnchecked 在同一线程串行执行,所以要注意Message对象
// 不要传递到子线程去处理,否则会导致多线程安全问题
msg.recycleUnchecked();
}
}
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;
}
/**
* 返回当前线程关联的Looper对象,如果调用线程与Looper无关则返回null
*/
public static @Nullable
Looper myLooper() {
return sThreadLocal.get();
}
/**
* 此方法用于返回关联当前线程的{@link MessageQueue} 对象,此方法必须被运行Looper的线程调用,否则会抛出空指针异常
*/
public static @NonNull
MessageQueue myQueue() {
return myLooper().mQueue;
}
/**
* 如果当前线程是此Looper关联的线程,则返回true
*/
public boolean isCurrentThread() {
return Thread.currentThread() == mThread;
}
/**
* 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;
}
/**
* 设置处理/分发耗时日志的阈值
* {@hide}
*/
public void setSlowLogThresholdMs(long slowDispatchThresholdMs, long slowDeliveryThresholdMs) {
mSlowDispatchThresholdMs = slowDispatchThresholdMs;
mSlowDeliveryThresholdMs = slowDeliveryThresholdMs;
}
/**
* 停止此Looper
* <p>
* 直接停止消息队列的消息处理且不再处理消息队列里面的任何消息
* 此方法可能会不安全,因为部分在队列里面可能没有处理Loop就停止了,建议采用quitSafely()
* </p>
*
* @see #quitSafely
*/
public void quit() {
mQueue.quit(false);
}
/**
* 安全停止此Looper,建设采用此方法
* 等所有队列中已存在的消息都分发处理后才会停止loop(),
* 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.
* 但是未到时间的delayed消息后面也不会分发给Handler处理了。
* </p>
* 任何尝试在调用quitSafely()方法后去post消息到队列中的尝试都会失败。比如{@link Handler#sendMessage(Message)} 会返回flase
*/
public void quitSafely() {
mQueue.quit(true);
}
/**
* Gets the Thread associated with this Looper.
*
* @return The looper's thread.
*/
public @NonNull
Thread getThread() {
return mThread;
}
/**
* 获取此Looper的消息队列
*
* @return 消息队列
*/
public @NonNull
MessageQueue getQueue() {
return mQueue;
}
@Override
public String toString() {
return "Looper (" + mThread.getName() + ", tid " + mThread.getId()
+ ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";
}
/**
* 观察者接口,在需要的时候进行
* {@hide}
*/
public interface Observer {
/**
* 在发送消息之前调用
*
* <p> 设计者并没有指定token类型以允许开发者指定自己的类型.
*
* @return 处理单个消息时用于收集telemetry的token,token必须精确地传递给
* {@link Observer#messageDispatched} or {@link Observer#dispatchingThrewException},而且不允许重复使用
*/
Object messageDispatchStarting();
/**
* 当消息被Handler处理时调用
* Called when a message was processed by a Handler.
*
* @param token 通过先前通过{@link Observer#messageDispatchStarting}在同一个观察者实例上获得
* @param msg 被处理的消息
*/
void messageDispatched(Object token, Message msg);
/**
* 但处理消息时抛出异常时调用.
*
* @param token token
* @param msg 被分发且导致出现异常的Message消息对象
* @param exception 出现的异常
*/
void dispatchingThrewException(Object token, Message msg, Exception exception);
}
}
2. Handler如何避免出现内存泄漏
-
在Activity的生命周期结束后,在
onDestory()
调用
mHandler.removeCallbacksAndMessages(null);
将所有的消息和回调都进行清理。(此方法仅用于清理仅在当前Context下创建的私有Handler,而不是主线程,否则会将其他场景产生的消息进行清除,导致功能异常) - 使用弱引用方法,此方法还未实践,待真正实践时再来记录。
3. Tips
Q: 如何知道当前线程是否是主线程
有关于UI的操作都要求在主线程执行,如下提供了通过Looper一种获取当前线程是否是主线程的方法:
public static boolean isMainThread(){
//获取主线程的Looper对象
Looper mainLooper = Looper.getMainLooper();
//获取主线线程的Looper对象
Looper currentLooper = Looper.myLooper();
return mainLooper== currentLooper;
}
版权声明:本文为hl1hl原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。