前言:
我们先宏观了解一下Handler通过sendMessage发送消息和处理消息的过程,如下所示,后面会详细分析:
handler.sendMessage -> messageQueue.enqueueMessage -> (looper.loop()) -> messageQueue.next() -> msg.target.dispatchMessage(msg) -> handler.handleMessage()
一、Handler源码解析
1、Handler构造方法
Handler的构造方法根据我们是否传入looper参数分为两类;
1)、当我们调用public Handler(Looper looper)和public Handler(Looper looper, Callback callback)构造方法时,会调用到如下构造方法,就是将我们传入的looper和callback赋给Handler中的成员变量:
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
复制代码
2)、当我们调用public Handler()或public Handler(Callback callback)构造方法时,会调用到如下构造方法,在注释1处可以看到通过Looper.myLooper()获取Looper并赋值为mLooper,Looper我们在下文会进行介绍:
public Handler(Callback callback, boolean async) {
、、、
mLooper = Looper.myLooper();//1
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
复制代码
2、Handler发送消息
当我们发送一条message时,在handler中的调用链如下:
sendMessage — sendMessageDelayed — sendMessageAtTime — enqueueMessage
最终调用到的enqueueMessage方法如下,有三步操作比较关键:
注释1处将message的target属性设为this,这个target属性有什么用呢?它在我们处理消息的时候会用到,用来标识这条msg被哪个handler处理;
注释2处会检查Handler的成员变量mAsynchronous如果为true,就将msg设置为异步消息,这个异步消息相关的知识我们在后面同步屏障的时候会讲;
注释3处就是调用了MessageQueue的enqueueMessage将msg放入消息队列;
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;//1
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);//2
}
return queue.enqueueMessage(msg, uptimeMillis);//3
}
复制代码
3、Handler处理消息
我们在后面分析looper的时候会看到,从loop方法中取出的消息会分发给handler处理,也就是调用到了dispatchMessage方法;
注释1处表示如果message的callback字段不为空,则会调用handleCallback,也就是调用message的callback的run方法;
注释2处表示如果handler的mCallback字段不为空,则会调用mCallback的handleMessage方法,我们可以在创建Handler的时候传入自定义的Callback;
注释3处表示前两个条件都为空,就会调用我们在handler中自己实现handleMessage(msg)方法;
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg); //1
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) { //2
return;
}
}
handleMessage(msg); //3
}
}
复制代码
二、MessageQueue源码解析
分析MessageQueue源码之前,我们需要知道Message的结构,Message类似于链表节点的结构,在其内部维护了一个next成员变量,类型也是Message,用于指向下一个Message;而在MessageQueue中维护了一个由链表实现的优先级队列,链表的节点就是Message;
1、enqueueMessage
在handler调用sendMessage最终调用到queue.enqueueMessage,方法如下所示;
注释1处表示队列中没有消息或者插入的消息等待时间为0或者等待时间小于队列头消息的等待时间,此时将新插入的消息放到队列头;
注释2处表示不满足这三种条件,则会在队列中插入到合适的位置;注释三处表示如果需要唤醒,则调用nativeWake;
boolean enqueueMessage(Message msg, long when) {
、、、
synchronized (this) {
、、、
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) { //1
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else { //2
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr); //3
}
}
return true;
}
复制代码
2、next
分析完enqueueMessage,我们先提前分析一下next方法,这个方法在什么地方被调用呢,就是在looper的loop方法中会调用,用于取出消息,主要源码如下;
注释1处的native方法nativePollOnce用于cpu休眠,这也就跟nativeWake对上了,取消息休眠,消息入队唤醒;并且消息入队和出队是都是在synchronized (this)代码块中,并且锁的都是this对象,因此发送消息或者取消息是互斥的,每次只有一个线程能操作;
注释2处用于处理异步消息,我们在后面介绍同步屏障时再分析;注释3处用于将得到的消息返回,在返回之前需要重新维护一下队列中的message;
Message next() {
、、、
int nextPollTimeoutMillis = 0;
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis); //1
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous()); //2
}
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg; //3
}
} else {
nextPollTimeoutMillis = -1; //4
}
}
nextPollTimeoutMillis = 0; //5
}
}
复制代码
三、Looper源码分析
我们在创建Handler时如果没有传入Looper,则会调用Looper.myLooper()获取Looper对象;我们在使用handler消息机制时,需要保证Looper已经创建并且开启loop循环来使消息机制正常运行,但是我们在主线程中使用handler时并没有进行这两步操作,那么这两步操作是在哪里执行的呢?
答案是在ActivityThread的main方法中执行,ActivityThread的main方法代表我们应用程序进程的主线程执行入口,main方法如下所示,注释1处就是创建Looper对象,注释2处就是开启loop循环;
public static void main(String[] args) {
、、、
Looper.prepareMainLooper(); //1
、、、
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
、、、
Looper.loop(); //2
throw new RuntimeException("Main thread loop unexpectedly exited");
}
复制代码
1、prepare方法介绍
在prepareMainLooper方法中调用prepare;
注释1处首先会判断,用来保证一个线程只能有一个Looper;
注释2处用来新建Looper对象,并将其设置进ThreadLocal中,保证线程隔离;quitAllowed参数表示looper是否可以退出,我们在主线程中调用的prepare将此参数传为fasle表示不可退出,我们在子线程中创建looper时会默认此参数为true表示可以退出,并且我们在子线程中用完一定要退出,否则引发内存泄漏;
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { //1
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed)); //2
}
复制代码
通过构造方法创建Looper如下所示,注释1处表示新建MessageQueue对象,因此Looper和MessageQueue都是对应每个线程的,并且每个线程都只有一个;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed); //1
mThread = Thread.currentThread();
}
复制代码
ThreadLocal的set方法如下所示,这里通过getMap获得每个线程的成员变量threadLocalMap,ThreadLocal和Looper是在ThreadLocalMap中的Entry数组中保存的,从而保证了线程的隔离;这里不详细介绍,在前面介绍ThreadLocal的文章中已经介绍过了;
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
复制代码
2、loop方法介绍
介绍完了prepare,我们也就创建完了Looper和MessageQueue并且将它们绑定到了对应线程上了;下面介绍loop方法用于从messageQueue中取消息;
注释1处从MessageQueue中取消息,两种情况会造成阻塞,一种是队列中没有消息,第二种是队列中最早的消息也还没到执行时间;
注释2处用于分发消息到handler去执行;
注释3处表示消息处理完了之后进行回收;
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
、、、
for (;;) {
Message msg = queue.next(); // might block //1
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
、、、
try {
msg.target.dispatchMessage(msg); //2
}
、、、
msg.recycleUnchecked(); //3
}
}
复制代码
3、quit方法介绍
当我们在子线程中创建了looper时,我们在用完之后一定要调用quit方法退出looper,否则会造成内存泄漏;quit方法只有一行代码,就是调用了MessageQueue的quit方法;
public void quit() {
mQueue.quit(false); //1
}
复制代码
我们接着分析MessageQueue的quit的方法;
注释1处表示主线程的looper不能退出;
注释2处将mQuitting变量置为true,并通过注释3唤醒队列;当我们将mQuitting置为true后,在MessageQueue的next方法就会返回null,然后我们在looper的loop方法中判断如果返回的msg为null,就会退出for循环,也就退出了loop;
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit."); //1
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true; //2
nativeWake(mPtr); //3
}
}
复制代码
4、其他方法分析
1)、getMainLooper用于返回主线程的looper;
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
复制代码
2)、如果我们在子线程中创建了looper,myLooper用于返回当前线程的looper;
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
复制代码
3)、myQueue用于返回当前线程对应的MessageQueue;
public static @NonNull MessageQueue myQueue() {
return myLooper().mQueue;
}
复制代码
4)、getThread用于返回当前Looper对应的线程;
public @NonNull Thread getThread() {
return mThread;
}
复制代码
4、Message源码分析
我们在前面也分析过,Message就是链表中的节点,内部有一个next成员变量,类型也是Message的,用于指向下一个Message;我们创建一个Message的时候一般通过new Message或者Message.obtain;
1、obtain方法解析
obtain方法如下所示,我们在Message中维护了一个Message池,sPool表示池中的第一个Message,如果sPool不为空,我们就复用sPool,并在注释2处将池中的第一个元素更新一下,并在注释3处将复用的Message的next置为null,从链表中去除;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool; //1
sPool = m.next; //2
m.next = null; //3
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
复制代码
2、recycleUnchecked方法解析
我们在looper的loop循环中取出消息,并分发给handler处理,当消息处理完之后,会调用msg.recycleUnchecked(),这就是执行的message回收;
如下所示,首先会将Message的各个字段清空,然后在注释1处将回收的message的next指向sPool;
在注释2处将sPool重新置为回收的此message,并在注释3处维护一下池中元素的个数;
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool; //1
sPool = this; //2
sPoolSize++; //3
}
}
}
复制代码
五、同步屏障
同步屏障,顾名思义,就是屏蔽同步消息,处理异步消息;
在MessageQueue的next方法中有如下这么一段代码,注释1处如果判断msg不为空,然后msg.target为空,则会触发同步屏障,
在注释2处的while循环中去寻找异步消息;我们在创建消息时可以调用setAsynchronous(true)将消息设置为异步消息;那么这个msg.target == null条件是怎么满足的呢?
if (msg != null && msg.target == null) { //1
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous()); //2
}
复制代码
开启同步屏障是通过调用postSyncBarrier方法,在注释1处我们发现创建的msg并没有给target字段赋值,因此为空,所以也就满足了同步屏障的条件;然后在下面会将这条msg插入到队列中;
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token; //1
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
复制代码
同步屏障用于处理一些紧急任务,我们通过开启同步屏障,然后将消息设为异步消息,我们处理完异步消息之后需要关闭同步屏障,否则在next取消息时同步消息取不出来了,那么怎么取消同步屏障呢?
取消同步屏障通过调用removeSyncBarrier,方法如下所示,我们是在注释1处或注释2处取消的同步屏障,这个p消息就是开启同步屏障的那个msg,我们通过prev.next = p.next或者mMessages = p.next将此消息从队列中移除。
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {
prev.next = p.next; //1
needWake = false;
} else {
mMessages = p.next; //2
needWake = mMessages == null || mMessages.target != null;
}
p.recycleUnchecked();
// If the loop is quitting then it is already awake.
// We can assume mPtr != 0 when mQuitting is false.
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
复制代码
六、同步屏障在系统中的典型应用
View更新时,draw、requestLayout、invalidate等很多地方都调用了ViewRootImpl#scheduleTraversals(),下面我们看看这个方法的实现,注释1处就是开启了同步屏障;注释2处的postCallback方法用于发送异步消息;
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); //1
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); //2
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
复制代码
postCallback方法最终会调用postCallbackDelayedInternal,如下所示,注释1处表示这是一个异步消息;
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
if (DEBUG_FRAMES) {
Log.d(TAG, "PostCallback: type=" + callbackType
+ ", action=" + action + ", token=" + token
+ ", delayMillis=" + delayMillis);
}
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
if (dueTime <= now) {
scheduleFrameLocked(now);
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
msg.arg1 = callbackType;
msg.setAsynchronous(true); //1
mHandler.sendMessageAtTime(msg, dueTime);
}
}
}
复制代码
我们处理完异步消息需要取消同步屏障,那么是在什么时候取消的呢?答案就是ViewRootImpl#unscheduleTraversals()方法,如下所示,在注释1处就是调用了取消同步屏障的方法;
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); //1
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
复制代码
七、子线程创建Looper的典型应用之IntentService
我们知道IntentService作为一种特殊的Service,可以自动在子线程执行我们重写的onHandleIntent方法,并且在执行完之后自动停止服务,我们下面分析一下如何实现的;
ServiceHandler是IntentService的内部类,在handleMessage方法中就实现了特有的逻辑,那么这个handleMessage什么时候会被调用呢?
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
复制代码
IntentService的onCreate和onStart方法如下所示,在注释1处创建了HandlerThread,然后在注释2处获得此线程对应的looper;注释3处通过创建的looper创建一个handler;
注释4处通过此handler发送消息,也就触发了我们上面的handleMessage方法,那么这个HandlerThread又是啥呢?我们在下一小节进行介绍。
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); //1
thread.start();
mServiceLooper = thread.getLooper(); //2
mServiceHandler = new ServiceHandler(mServiceLooper); //3
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg); //4
}
复制代码
八、子线程创建Looper的典型应用之HandlerThread
HandlerThread的run方法如下所示,当我们在IntentService中调用此线程的start方法时会调用此run方法,可以看到注释1和注释3分别调用了prepare和loop,这也是在子线程创建looper的关键;注释2处的加锁用于和getLooper方法配合;
public void run() {
mTid = Process.myTid();
Looper.prepare(); //1
synchronized (this) { //2
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop(); //3
mTid = -1;
}
复制代码
getLooper方法如下所示,注释1处的锁和run方法配合,注释2表示当mLooper还为空时需要调用wait释放锁并等待,然后在run方法中给mLooper赋值之后调用notifyAll唤醒wait;
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) { //1
while (isAlive() && mLooper == null) {
try {
wait(); //2
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
复制代码