Android面试之Handler简述
作为Android开发者,在面试的过程中,handler这个知识点很大概率会被问到,今天通过源码来了解下这个handler。
Looper和MessageQueue
准备
要了解Handler的原理,我们先来了解两个类,分别是Looper,MessageQueue。因为只有这两个类都准备好的前提下,handler才有作用。
在Android应用中,我们都知道,其入口是ActivityThread的main函数
public static void main(String[] args) {
...
Looper.prepareMainLooper();
...
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
首先我们跟进prepareMainLooper
函数内部,代码并不多,主要是创建Looper。
相关代码如下:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
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));
}
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其内部有个mQueue的变量是MessageQueue类型的,此时Looper,MessageQueue都已经准备好了。
Looper和MessageQueue如何运行的
回到main函数的Looper.loop();
这个方法
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.");
}
...
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
首先第2~4行,异常message说的很明确,同时前面笔者所说的只有在Looper和MessageQueue都准备好,handler才有效,也是这个原因。而且MessageQueue是Looper中的一个成员变量,所以Looper.prepare()内部都已准备好。
最后是一个for循环,再跟进去看,我们暂时只关注handler相关的部份,主要代码如下:
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
...
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);
}
}
...
msg.recycleUnchecked();
return true;
}
通过MessageQueue.next方法取出消息,对该消息进行处理,并在最后释放消息内容。
到这里,我们了解到了,在Android应用程序启动是,首先调用了Looper.prepareMainLooper进行主线程Looper的准备工作,然后在调用Looper的loop
方法进入死循环,在循环内部通过MessageQueue的next
方法获取消息并进行处理。在Looper中值得注意的是两个变量,分别是sThreadLocal
和mQueue
,sThreadLocal
是保证了当前looper只属于当前线程,而mQueue
是looper的消息队列。
注: 留意这里的
msg.target.dispatchMessage(msg);
,后面会提到
Handler的原理
Handler的使用
首先我们需要初始化一个Handler
private val handler = Handler(Looper.getMainLooper()) {
println("===== callback thread: ${Thread.currentThread().name}")
println("===== ${it.what}")
println("===== ${it.data.getString("Content")}")
true
}
在子线程send messge
GlobalScope.launch(Dispatchers.IO) {
println("===== send thread: ${Thread.currentThread().name}")
handler.sendMessage(Message.obtain().apply {
this.data = bundleOf("Content" to "Hello world!!!")
this.what = 1
});
}
注:这里的代码在Activity/Fragment中的,handler也是通过主线程创建的
现在我们知道handler如何使用了,接下来跟踪代码来理解,首先来看看sendMessage
方法,相关代码全部贴出来
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
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);
}
看到最终调用的是enqueueMessage
方法,注意msg.target = this;
和queue.enqueueMessage(msg, uptimeMillis);
先来看queue.enqueueMessage(msg, uptimeMillis);
,这个queue就是mQueue,而mQueue在初始化Handler的时候赋值的mQueue = mLooper.mQueue;
,也就是说调用sendMessage方法最终会通过looper的messageQueue将这个消息入队列之中。再根据前面我们说的,loop函数会一直查看消息队列的消息,出队列时做处理。
现在看msg.target = this;
,为什么要关注这个呢? 首先这个方法是在Handler类之中的,这个this指的就是当前的handler。
前文提到looper会冲消息队列通过next出队并处理,其中就有一行代码msg.target.dispatchMessage(msg);
现在来看Handler的dispatchMessage(msg)
方法
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
使用例子中的case,msg是没有传callback的,所以其实这里调用了mCallback.handleMessage(msg)。也就是我们创建Handler实例的回调。
到这里,Handler与Looper、MessageQueue、Message之间的关系讲完了。
扩展
子线程能否使用handler?如果可以需要注意什么?如果没有消息后应该如何处理?
Handler其实就是一个类,当然可以在子线程中使用。子线程handler创建的时候一定需要设定一个Looper给这个handler(Looper.prepare()),否则会在调用loop() 的时候抛出Runtime异常。主要是如下代码if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); }
当没有消息的时候,我们应该调用looper.quit() 方法释放资源,否则会因为死循环导致线程内存泄漏。
Handler在等待新消息的时候会阻塞吗? 如果会,那会不会导致ANR?
Handler在等待新消息的时候其实就是在死循环的过程,也称为looper阻塞。所以是会阻塞的。但不会导致ANR,因为Android系统本来就是事件驱动的,looper.loop不断接受事件和处理事件,如果它停止了,App也就停止了,所以这个问题本身就是假命题。
另一种说法就是ANR本来就是类似定时炸弹一样的设计,在事件发送时埋下定时炸弹,如果在规定的时间内没有收到处理完的通知,则判定为超时,引爆炸弹(ANR),收到则拆除炸弹。而Looper在loop的过程并没埋下这个定时炸弹
总结
通过跟踪源码,我们清晰了解到了Handler处理消息的过程。
- 首先在程序入口调用Looper.prepareMainLooper()方法,提供主线程Looper和MessageQueue
- 然后继续调用Looper的loop方法进入死循环,通过MessageQueue的next方法取得消息并处理
- 取得的消息怎么来的? Handler send/post(post方式会传入Runable给message)来的
- 在send过程中,handler会被赋给message.target
- 取得的消息中,Message拿到target,即为handler,处理消息就是通过handler.dispatchMessage方法调用回调方法返回主线程处理(因为这里的handler是在主线程创建的)