解析Handler消息机制(Android12)

Android的知识体系搭建

一 概述

在我们之前介绍四大组件的过程中,经常会看到两类代码。一个就是 mService(AMS) 和 mThread(ApplicationThread) 的调用,它们分别代表了应用进程通过 mService 与 AMS 的通信,和 AMS 通过 mThread 向应用进程的通信。

然而,在这个通信过程中,我们还会经常看到另外一类代码,它们就是 scheduleTransaction 之类。它们是在经过了进程间的通信之后,从进程间通信的 binder 线程,切换到应用的 UI 线程的线程间的通信方式。

所以,说到底,Handler 其实就是 Android 用来在线程间通信的一种方式。今天,我们就来说一说,应用的线程间通信的方式,即 Handler 机制。

二 线程间通信

2.1 线程与同步

既然我们说 Handler 是 Android 用来进行线程间通信的一种方式,那么我们就先来说说 Android 是如何使用 Handler 进行线程间通信的。

Handler和Looper.png

首先,在我们的主线程中,有一个 Looper,这个 Looper 会不断的从一个 MessageQueue 中取出 Message,这些 Message 就表示对应的回调。然后在其他的线程,可以通过 Handler 发送消息,这样主线程中的 Looper 就会取到对应的 Message,然后执行回调。

那么,大家想过这样一个问题没有,Android 的 Handler 通信,是如何保证不同线程间的同步的?

首先,我们可以查看主线程代表的类 ActivityThread。

[frameworks/base/core/java/android/app/ActivityThread.java]

@UnsupportedAppUsage
final Looper mLooper = Looper.myLooper();
@UnsupportedAppUsage
final H mH = new H();

复制代码

大家有没有想过这样一个问题?为什么这里的 mLooper 和 mH 都是 final 的。

其实这是 Java 并发编程中的知识,对于 Java 的多线程同步,一般有两种方式,其一就是使用 synchronized 或者锁,另一种则是使用不变性。最为代表的就是 final。这样就能保证不论是哪个线程调用到了 ActivityThread,它们拿到的 mLooper 和 mH 都是相同的。

2.2 Handler 的线程间同步

然后我们再简单看一下 Handler 中发送消息的逻辑。源码相关的解析我们放后面,我们直接看最终的调用函数 sendMessageAtTime。

[frameworks/base/core/java/android/os/Handler.java]

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
	MessageQueue queue = mQueue;
	if (queue == null) {
		return false;
	}
	return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
		long uptimeMillis) {
	msg.target = this;
	msg.workSourceUid = ThreadLocalWorkSource.getUid();

	if (mAsynchronous) {
		msg.setAsynchronous(true);
	}
	return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码

在 sendMessageAtTime 中,将 Handler 的成员变量 mQueue 取出,然后调用了它的 enqueueMessage 函数。前面我们说了,在 ActivityThread 中,通过定义 mH 为 final 保证了不同线程拿到的 Handler 都是相同的。

2.3 MessageQueue 的线程间同步

但是,在这里就算是同一个 Handler,依旧不能保证它的成员变量都是一样的,Android 又是如何保证不同线程的 Handler,它的 MessageQueue 也是同一个呢?

相比熟悉 JUC 的小伙伴已经猜到了,这里依旧使用了不变性,final 关键字。

[frameworks/base/core/java/android/os/Handler.java]

@UnsupportedAppUsage
final Looper mLooper;
final MessageQueue mQueue;
@UnsupportedAppUsage
final Callback mCallback;
final boolean mAsynchronous;
复制代码

Looper 和 MessageQueue 都是 final,这样又保证了 Handler 中,不同线程拿到的 MessageQueue 的唯一性,又保证了多线程间的同步。

好了,最后一个问题,MessageQueue 又是怎么保证线程间的同步的呢?我们之前已经说了,MessageQueue 中有一个消息队列,Looper 会不断的轮询这个消息队列,然后取出对应的 Message 执行回调。

这里我们不卖关子,直接看代码。

[frameworks/base/core/java/android/os/MessageQueue.java]

boolean enqueueMessage(Message msg, long when) {
	if (msg.target == null) {
		throw new IllegalArgumentException("Message must have a target.");
	}

	synchronized (this) {
		...
	}
}

复制代码

在 MessageQueue 的 enqueueMessage 中,除了一开始对 Message 的 target 判断,后面直接使用了 synchronized 关键字,这样就又保证了不同线程间,消息队列的线程间同步。

到此,我们就基本知道了,Handler,Looper 还有 MessageQueue 是如何保证线程间同步的。

2.4 Handler 架构

接下来,我们简单介绍一个 Handler 架构,其实从之前的介绍,我们已经介绍的差不多了。

Handler.jpg

Handler 中有 Looper 有 MessageQueue,并且还都是 final 标识的。 Looper 中有 MessageQueue,用来轮询。它们的 MessageQueue 都是同一个。

接下来,我们从源码的角度,来具体分析 Handler 机制。

三 Handler

3.1 Handler 的初始化

在之前我们已经了解到了 Android 进程的启动流程,并且在进程的启动过程中,我们看到了 Handler 的初始化。

[frameworks/base/core/java/android/app/ActivityThread.java]

public static void main(String[] args) {
   
	Looper.prepareMainLooper();

	// 创建 ActivityThread 
	ActivityThread thread = new ActivityThread();
	thread.attach(false, startSeq);

	if (sMainThreadHandler == null) {
		// 调用 ActivityThread 的 getHandler
		// 
		sMainThreadHandler = thread.getHandler();
	}

	Looper.loop();

	throw new RuntimeException("Main thread loop unexpectedly exited");
}

复制代码
// ActivityThread 的成员变量
// H 是 Handler 的子类
final H mH = new H();

public Handler getHandler() {
	return mH;
}
复制代码

在进程的启动过程中,会创建一个 Handler 和一个 Looper,并对其进行初始化。

public Handler() {
	this(null, false);
}
复制代码
[frameworks/base/core/java/android/os/Handler.java]

public Handler(@Nullable Callback callback, boolean async) {
	...

	mLooper = Looper.myLooper();
	...
	mQueue = mLooper.mQueue;
	mCallback = callback;
	mAsynchronous = async;
}
复制代码

3.1 Looper

[frameworks/base/core/java/android/os/Looper.java]

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

public static @Nullable Looper myLooper() {
	return sThreadLocal.get();
}

复制代码

3.3 prepareMainLooper

在使用 Looper 之前,ActivityThread 中会调用 prepareMainLooper,主要目的就是为当前的线程准备一个 Looper,并保存到当前线程的 ThreadLocal 中。

public static void prepareMainLooper() {
	// prepareMainLooper 传入参数是 false
	prepare(false);
	synchronized (Looper.class) {
		if (sMainLooper != null) {
			throw new IllegalStateException("The main Looper has already been prepared.");
		}
		sMainLooper = myLooper();
	}
}

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));
}
复制代码

prepareMainLooper 其实就是向当前线程对应的 ThreadLocal<Looper>中保存一个 Looper。

3.4 Looper 的构造函数

private Looper(boolean quitAllowed) {
	mQueue = new MessageQueue(quitAllowed);
	mThread = Thread.currentThread();
}
复制代码

在 Looper 的构造函数中,创建了一个 MessageQueue 的成员变量 mQueue。然后就是开始轮询 loop -> loopOnce。构造函数传入的参数 quitAllowed 表示这个 Looper 是否可以退出,一般主线程创建的 Looper 是不能退出的,所以传入的参数是 false。

3.5 loop

public static void loop() {
	// 拿到当前线程(main线程)的 Looper
	final Looper me = myLooper();

	// 如果 me 为空就抛出异常,
	if (me == null) {
		throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
	}

	me.mInLoop = true;

	// 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'


	me.mSlowDeliveryDetected = false;

	for (;;) {
		if (!loopOnce(me, ident, thresholdOverride)) {
			return;
		}
	}
}

复制代码

3.6 loopOnce

private static boolean loopOnce(final Looper me,
		final long ident, final int thresholdOverride) {
		
	// 从消息队列中取出下一条 Message,阻塞函数
	Message msg = me.mQueue.next(); // might block
	if (msg == null) {
		// No message indicates that the message queue is quitting.
		return false;
	}

	// 打印开始消息分发
	final Printer logging = me.mLogging;
	if (logging != null) {
		logging.println(">>>>> Dispatching to " + msg.target + " "
				+ msg.callback + ": " + msg.what);
	}
	// 消息分发的观察者,这个是高版本加入的
	final Observer observer = sObserver;

	...
	
	Object token = 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;
	} 
	
	...

	// 打印消息,消息分发结束
	if (logging != null) {
		logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
	}

	msg.recycleUnchecked();

	return true;
}

复制代码

loopOnce 其实就是从消息队列中取出一条 Message,然后进行分发,而取出消息的逻辑在 MessageQueue 中,并且 next 也是一个阻塞函数。

3.7 dispatchMessage

取出消息之后则是通过 dispatchMessage 进行分发。

public void dispatchMessage(@NonNull Message msg) {
	if (msg.callback != null) {
		handleCallback(msg);
	} else {
		if (mCallback != null) {
			if (mCallback.handleMessage(msg)) {
				return;
			}
		}
		handleMessage(msg);
	}
}
复制代码

接下来我们看看具体消息是如何取出的。

四 MessageQueue

4.1 next

[frameworks/base/core/java/android/os/MessageQueue.java]

@UnsupportedAppUsage
Message next() {
	// 如果 MessageQueue 已经退出的话,mPtr 就会被设置为 0
	// 这样就直接返回
	final long ptr = mPtr;
	if (ptr == 0) {
		return null;
	}

	// 每次调用 next,都会将 Idle Handlers 的数量先设置为-1
	int pendingIdleHandlerCount = -1;
	int nextPollTimeoutMillis = 0;
	
	for (;;) {
		if (nextPollTimeoutMillis != 0) {
			Binder.flushPendingCommands();
		}
		
		// 调用 nativePollOnce,阻塞等待
		nativePollOnce(ptr, nextPollTimeoutMillis);

		synchronized (this) {

			final long now = SystemClock.uptimeMillis();
			Message prevMsg = null;
			Message msg = mMessages;
			if (msg != null && msg.target == null) {
				// 如果 msg 不为空,并且没有 target
				do {
					// 如果 msg 是异步消息,就会一直向后查找
					// 也就是说,如果存在异步消息,最先处理异步消息
					prevMsg = msg;
					msg = msg.next;
				} while (msg != null && !msg.isAsynchronous());
			}
			
			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;
				}
			} else {
				// No more messages.
				nextPollTimeoutMillis = -1;
			}

			// Process the quit message now that all pending messages have been handled.
			if (mQuitting) {
				dispose();
				return null;
			}

			// 如果 pendingIdleHandlerCount 小于 0,即第一次运行
			// 并且 mMessages 为空或者当前时间小于消息处理的时间,就获取 Idle Handlers 的数量
			if (pendingIdleHandlerCount < 0
					&& (mMessages == null || now < mMessages.when)) {
				pendingIdleHandlerCount = mIdleHandlers.size();
			}
			
			if (pendingIdleHandlerCount <= 0) {
				// 没有 Idle Handlers 就跳出此次循环
				mBlocked = true;
				continue;
			}

			// 如果有 Idle Handlers,就创建一个 Idle Handlers 的数组
			if (mPendingIdleHandlers == null) {
				mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
			}
			mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
		}

		// 循环处理 Idle Handlers
		for (int i = 0; i < pendingIdleHandlerCount; i++) {
			final IdleHandler idler = mPendingIdleHandlers[i];
			mPendingIdleHandlers[i] = null; // release the reference to the handler

			boolean keep = false;
			try {
				// 处理 Idle Handlers,并且返回 keep
				keep = idler.queueIdle();
			} catch (Throwable t) {
				Log.wtf(TAG, "IdleHandler threw exception", t);
			}

			// 如果 keep 为 false,就移除掉这个 Idle Handler
			if (!keep) {
				synchronized (this) {
					mIdleHandlers.remove(idler);
				}
			}
		}

		// 重置 Idle Handlers 的数量
		pendingIdleHandlerCount = 0;

		nextPollTimeoutMillis = 0;
	}
}

复制代码

在 MessageQueue 的 next 中,有着如下的处理逻辑

  1. 首先调用 nativePollOnce 进入阻塞
  2. 拿到 Message 列表的第一个消息
  3. 如果队头的消息 target 为空,就查找消息列表中的异步消息
  4. 如果队头的消息 target 不为空,就判断最前面的消息的处理时间是否已经到了,如果到了,就直接处理,如果没有到,就进入休眠,并休眠对应的时间
  5. 如果消息队列处理完毕了,就看是否存在 Idle Handler,如果存在,就处理,如果不存在,就退出循环,等待下一次 next。

在 MessageQueue 的消息队列轮询中,我们看到了系统首先会处理异步消息(如果存在的话),然后再处理普通消息,最后处理 Idle 消息。

到这里,关于 Handler 消息机制 Java 层的原理我们就已经介绍完了。接下来我们再来看看刚才的三种消息,是如何发出的。

  • 异步消息
  • 普通消息
  • Idle 消息

五 普通 Handler 消息的发送

5.1 sendMessage

public final boolean sendMessage(@NonNull Message msg) {
	return sendMessageDelayed(msg, 0);
}
复制代码

5.2 sendMessageDelayed

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
	if (delayMillis < 0) {
		delayMillis = 0;
	}
	return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
复制代码

5.3 sendMessageAtTime

public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
	// 拿到成员变量 mQueue
	MessageQueue queue = mQueue;
	if (queue == null) {
		...
		return false;
	}
	return enqueueMessage(queue, msg, uptimeMillis);
}
复制代码

5.4 enqueueMessage

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
		long uptimeMillis) {
	msg.target = this;
	msg.workSourceUid = ThreadLocalWorkSource.getUid();

	if (mAsynchronous) {
		msg.setAsynchronous(true);
	}
	return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码

5.5 MessageQueue.enqueueMessage

[frameworks/base/core/java/android/os/MessageQueue.java]

boolean enqueueMessage(Message msg, long when) {
	
	if (msg.target == null) {
		throw new IllegalArgumentException("Message must have a target.");
	}

	synchronized (this) {
		...

		// 首先会进行三个异常判断,然后将消息的处理时间保存到 when 中
		msg.markInUse();
		msg.when = when;
		
		// 这是一个链表
		Message p = mMessages;
		boolean needWake;
		
		if (p == null || when == 0 || when < p.when) {
			// 插入队头
			msg.next = p;
			mMessages = msg;
			// mBlocked 只有在之前队列处理完所有当前时间需要处理的消息之后,才会为 true
			needWake = mBlocked;
		} else {
			// 插入队伍中间
			// 只有mBlocked为true,并且还是异步消息,才需要唤醒
			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);
		}
	}
	return true;
}
复制代码

在 Android 中,所有发送普通消息的逻辑,最后都会走到 enqueueMessage 这个函数,而这个函数中,首先就会判断 target 是否为空,然后再进行消息插入。

这里我们还需要注意插入消息还有一个是否唤醒的判断,需要唤醒的条件只有一种,那就是当前的消息队列,已经处理完了当前时间需要处理的消息

什么是当前时间需要处理的消息?假如我们当前的时间戳为 100,而消息队列中,需要处理的消息都有对应的时间戳,其中时间戳为 100 的我们已经处理完了,消息头需要处理的消息对应时间戳为 200。这种情况下,消息轮询会休眠一段时间,避免消耗 CPU。所以这时如果我们向消息队列添加了消息,就可能需要唤醒。

具体是否唤醒,会做这样两个判断

  1. 消息是否插入的是消息头。(如果是消息头,那么就是最早需要处理的消息,则唤醒一次看看,如果到了时间就处理,如果没有到时间就计算一个休眠时间,接着休眠)
  2. 如果不是消息头,那么就看是否是异步消息,如果是异步消息才需要唤醒。

接下来我们再来说说这其中所谓的异步消息是怎么发出来的。首先,正常情况下,我们是发送不出 target 为空的消息的,那么,target 为空的消息是怎么发送出来的呢?

我们直接看 Android 的刷新逻辑,在 ViewRootImpl 中,有着这样一段代码

六 ViewRootImpl

6.1 scheduleTraversals

[frameworks/base/core/java/android/view/ViewRootImpl.java]

void scheduleTraversals() {
	if (!mTraversalScheduled) {
		mTraversalScheduled = true;
		// 首先是向消息队列中,插入一个消息屏障
		mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
		// 然后发送一条异步消息
		mChoreographer.postCallback(
				Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
		notifyRendererOfFramePending();
		pokeDrawLockIfNeeded();
	}
}
复制代码

6.2 postSyncBarrier

[frameworks/base/core/java/android/os/MessageQueue.java]

@UnsupportedAppUsage
@TestApi
public int postSyncBarrier() {
	return postSyncBarrier(SystemClock.uptimeMillis());
}

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.arg 1 = token;

		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;
	}
}

复制代码

postSyncBarrier 其实就是发送消息屏障的意思,在 postSyncBarrier 函数上,有一个 UnsupportedAppUsage 的注解,也就是说发生消息屏障正常情况下是不会提供给应用端使用的。

而消息屏障其实就是一条没有 target 的消息,其实这也很好理解,消息屏障会暂停普通消息的处理,转而处理异步消息,这样势必会导致普通消息的处理延迟。在我们之前解析四大组件的时候,介绍过,在四大组件的启动过程中,会用到 Handler 机制,并且还有延迟消息来计算 ANR,而不合理的异步消息,势必会导致 ANR 的发生,所以 Android 不让应用端发生异步消息也是很好理解的。

6.3 postCallbackDelayedInternal

private void postCallbackDelayedInternal(int callbackType,
		Object action, Object token, long 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 {
			// 发送一条异步消息,setAsynchronous 为 true
			Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
			msg.arg 1 = callbackType;
			msg.setAsynchronous(true);
			mHandler.sendMessageAtTime(msg, dueTime);
		}
	}
}

复制代码

最后,我们可以看到,异步消息其实就是 setAsynchronous 为 true 的消息,和其他的消息也没有什么不同。

猜你喜欢

转载自juejin.im/post/7219869424551362616