写给Rikka自己的Handler源码说明书,安卓开发项目经历

本文详细解读了Android中Handler的源码,包括Looper的loop()方法、MessageQueue的next()方法以及Message的处理流程。通过源码分析解答了为何Handler开启死循环不会导致卡顿和资源浪费,同时介绍了Message如何被加入到MessageQueue中以及消息分发的过程。此外,文中通过实例展示了如何在子线程中更新UI,以及主线程如何更新子线程和子线程间如何互相通信。
摘要由CSDN通过智能技术生成

来看下 loop()代码:

// Looper.java

public static void loop() {

final Looper me = myLooper(); // 1

final MessageQueue queue = me.mQueue; // 2

for (;😉 { // 3

Message msg = queue.next(); // 4

if (msg == null) { // 5

// No message indicates that the message queue is quitting.

return;

}

final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

try {

msg.target.dispatchMessage(msg); // 6

} finally {

}

msg.recycleUnchecked();

}

}

上面滤去了不重要的代码。

注释1:拿到当前线程的Looper(即ui线程的Looper)

注释2:从Looper中拿出 MessageQueue

注释3:开启死循环

注释4:从 MessageQueue.next()中获取一个 Message出来

注释5:如果取出的Message为null,则跳出死循环

注释6:根据 Message的target来通过 dispatchMessage(msg)来发送这个消息。

也就是说 Looper.loop()开启了一个死循环,来处理其 MessageQueue里面的每一个Message。

直到 Message为null了才跳出循环,而英文注释表示 当只有在 MessageQueue退出的时候 Message才为空,而MessageQueue的生命和线程一样,也就是说:线程终止了,这个循环才会结束

所以也从而验证了,这个类为什么叫Looper(循环者,不是某冠军上单)。

这里有两个问题,也是面试的时候面试官喜欢问的:

(1)为什么开了死循环,App却感受不了卡顿,也没有ANR?

逆推回去,既然App没有卡顿,也就是说 MessageQueue有源源不断的message供主线程处理,这是因为像 AMS、WMS这些SystemServer进程在程序运行时会一直提供功能的支持,通过Binder机制,向主进程的主线程中发送消息,所以 Looper的死循环一直是工作状态,所以并不会导致卡顿。

(2)那这样一直开着死循环,不会很占CPU资源吗?

这里就要继续看代码了,我们看看上述循环中 MessageQueue.next()做了什么.

2.4 MessageQueue.next()


因为 next()代码有点长,所以把其中死循环的部分分成三个部分进行讲解:

Part1 无消息处理时挂起

// MessageQueue.java

private native void nativePollOnce(long ptr, int timeoutMillis);

Message next() {

int pendingIdleHandlerCount = -1;

int nextPollTimeoutMillis = 0;

for (;😉 { // 1

if (nextPollTimeoutMillis != 0) {

Binder.flushPendingCommands();

}

nativePollOnce(ptr, nextPollTimeoutMillis); // 2

}

}

注释1中:可以看到的, next()也开了个死循环,WTF?什么鬼,loop()已经是死循环,这里还来套一个死循环了???

注释2:调用 nativePollOnce(ptr, nextPollTimeoutMillis),这个方法非常滴重要,它是一个 native方法,我这里就不再深入到 JNI层去看C的代码了,我这边用大白话来解释这个代码的作用:

在主线程打开Looper循环前,会打开Android系统底层的一个I/O管道(Linux epoll),如果你身边有个后台人员,你可以问他关于 epoll的知识,epoll做的事情就是监视文件描述符的I/O事件,它是 事件驱动模型,换言之,它就是一个NIO。在没有事件时主线程挂起,有事件时主线程唤醒处理。

关于C++层的源码可以参考下这一篇:Android 中 MessageQueue 的 nativePollOnce

函数中传入了 nextPollTimeoutMillis,有点像 Thread.sleep(mills)。这个nextPollTimeoutMillis会在延时任务中起到作用。

这里也就回答了上节末尾的问题,为什么开死循环不会特别占用CPU的资源,是因为在没有消息的时候主线程已经挂起,有消息时才会唤醒。

Part2 返回一个消息

// MessageQueue.java

synchronized (this) {

final long now = SystemClock.uptimeMillis();

Message prevMsg = null;

Message msg = mMessages; // 1

if (msg != null) {

if (now < msg.when) { // 2

nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);

} else {

mBlocked = false;

if (prevMsg != null) {

prevMsg.next = msg.next;

} else {

mMessages = msg.next;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值