Android Handler消息机制02-Looper源码学习
Posted 双木青橙
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Handler消息机制02-Looper源码学习相关的知识,希望对你有一定的参考价值。
1.源码
本文主要是对Looper类的源码进行解析学习,用于更深入的理解Handler消息机制
Looper的源码路径为:android.os.Looper
1.2 典型案例
如下展示了一个源码中提供的典型实例
但是如下所示:此展示的是在一个子线程构建一个Handler,但是在实际开发中并没有必须在子线程这样做。一般来说Handler机制是用于子线程往主线程传递消息用于UI更新操作。
/**
* 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
@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;
/**
* 初始化当前线程为一个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;
private Looper(boolean quitAllowed)
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread(); //mThread置为当前线程
/**
* 如果当前线程是此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);
将所有的消息和回调都进行清理。 - 使用弱引用方法,此方法还未实践,待真正实践时再来记录。
以上是关于Android Handler消息机制02-Looper源码学习的主要内容,如果未能解决你的问题,请参考以下文章