更详细的分析可以看看这位大神的(http://gityuan.com/2015/12/26/handler-message-framework/)
网络上有很多分析了,本文章主要分析主要过程和关键技术点,以下涉及的代码都是基于Android8.0
Android Handler机制主要涉及Java层和native层先说说Java层的吧,要说清楚Java层的Handler机制,必须先要弄清楚以下几者的关系,Looper,MessageQueue,Message,Handler,ThreadLocal。
量的关系
每一个线程只会包含一个Looper对象,放在ThreadLocal 中;
每一个Looper只包含一个MessageQueue ,在Looper创建时创建
每个MessageQueue 由多个Message组成一个队列
每个Message都有一个分发的target对象Handler (除消息屏障,不过应用层没有开放)
如何启动Handler机制
Looper的创建
要启动整个Handler机制,必须先创建Looper对象,为什么呢?我们在使用Handler时,并没有去new 一个Looper对象啊。这是因为系统在创建一个app进程时,主动帮主线程创建了Looper对象,我们可以在framework/base/core/java/android/app/ActivityThread.java 文件中发现代码片段
public static void main(String[] args) {
......
Looper.prepareMainLooper();//创建一个主线程的Looper,该Looper不能退出
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();//开始循环接收各种消息
......
}
PS.如果不清楚ActivityThread在系统fork创建APP进程时所担任的角色,建议先熟悉下APP的启动流程,可以参考这位大神的(http://gityuan.com/archive/)
我们来看看Looper.prepareMainLooper() 方法
framework/base/core/java/android/os/Looper.java
public static void prepareMainLooper() {
prepare(false);//创建一个不能退出的Looper对象,并将该对象保存到当前线程
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();//从当前线程get一个Looper对象
}
}
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对象保存到当前线程
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();//从当前线程获取Looper对象
}
Looper对象已有了,再来看看 Looper.loop() 这个方法
framework/base/core/java/android/os/Looper.java
public static void loop() {
final Looper me = myLooper();
if (me == null) {//先检查下当前线程有没有Looper对象
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//该MessageQueue 对象是在创建Looper对象时在构造方法里初始化的
final MessageQueue queue = me.mQueue;
Binder.clearCallingIdentity();//清除权限校验
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // 取消息,可能会阻塞
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
......
......
try {
msg.target.dispatchMessage(msg);//开始分发,此处的target其实是一个Handler对象
} finally {
.............
}
........
}
}
以上过程大致描述清楚了主线程如何创建Looper对象,如何启用Handler机制,也回答了我们为什么不用new Looper对象也可以使用Handler,但是我们还有问题没解决。
1.为什么在主线程for死循环,应用APP不会ANR
首先要弄清楚这个问题之前,我们应该了解,Android有大量的消息驱动方式来进行交互,比如Android的四剑客Activity, Service, Broadcast, ContentProvider的启动过程的交互,都离不开消息机制,Android某种意义上也可以说成是一个以消息驱动的系统,在此背景下,我们应该可以感性的回答下上面说的问题
感性的分析
既然靠消息驱动,没有消息时,应用肯定处于空闲状态,这与应用无响应有本质的区别,无响应是应用响应超时,而应用是空闲的,肯定不会ANR了,如果有大量的消息,那么应用就处理这些消息,比如来了一个点击屏幕的消息,一个按键消息等等,当处理某个消息耗时超过系统定义的接受时长,应用可能ANR,,所以应用会不会ANR,与for死循环关系不大,与处理消息的速度有关。
作为一个有担当的有颜值的程序员,我们肯定希望理性的弄清楚这个问题的本质
理性的分析
先自问
1.ANR产生的场景
2.消息分发机制
ANR 产生场景我会在另外一篇文章中分析,先说下Java层消息分发机制
APP如何分发消息
APP不但分发handler 消息,也分发屏幕输入消息,屏幕事件消息等等,这些所有的消息序列构成APP与用户交互的桥梁
为了简化分析量,本案例暂时以Handler消息机制为切入点
Message msg = queue.next(); // 取消息,可能会阻塞
framework/base/core/java/android/os/MessageQueue.java
因为MessageQueue 这个对象实现了消息的接受与分发逻辑,而且与Native层的消息机制关联,因此我们要着重分析
首先我们来看看MessageQueue的主要属性与方法
// True if the message queue can be quit.
private final boolean mQuitAllowed;
@SuppressWarnings("unused")
//其实是一个native层MessageQueue 的句柄,frameworks/base/core/android_os_MessageQueue.cpp
private long mPtr; // used by native code,
Message mMessages;
//空闲时处理的Handler
private final ArrayList mIdleHandlers = new ArrayList();
private SparseArray mFileDescriptorRecords;
private IdleHandler[] mPendingIdleHandlers;
private boolean mQuitting;
// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
private boolean mBlocked;
//消息屏障,未对应用层开放,只能通过反射使用
private int mNextBarrierToken;
//会初始化native层的MessageQueue
private native static long nativeInit();
private native static void nativeDestroy(long ptr);
//通过Native层的MessageQueue阻塞nextPollTimeoutMillis毫秒的时间
//1.如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
//2.如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
//3.如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
//唤醒线程
private native static void nativeWake(long ptr);
//是否还在轮询
private native static boolean nativeIsPolling(long ptr);
//设置一个描述符,这里说明使用Looper可以通过设置描述符的方式来跨进程通讯
private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
我们来继续看看MessageQueue 中的next方法
final long ptr = mPtr;//native 层的MessageQueue
if (ptr == 0) {
return null;
}
//期望处理的IdelHandler数量
int pendingIdleHandlerCount = -1; // -1 only during first iteration
//下一次轮询标识
// 1.如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
//2.如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
//3.如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
//将当前线程中所有待处理的Binder命令刷新到内核*驱动程序,这样做的目的是防止因为Binder调用而产生的耗时,导致消息轮询时间延迟
Binder.flushPendingCommands();
}
//nextPollTimeoutMillis 值不同,这个地方可能会阻塞参看上面关于nextPollTimeoutMillis的注释
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//如果消息target为null,这代表该消息是个消息屏障
if (msg != null && msg.target == null) {
// 如果有消息屏障,就一直轮询取下一个消息,直到碰到一个异步消息或者下一个消息为null,说明消息屏障后面的消息最终只会取最后一个或者异步消息才能执行
do {
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;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 说明没有消息了,进入无限休眠,直到新的消息加入队列唤醒线程
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
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 {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
这个next方法流程涉及3种类型消息,一种是消息屏障,一种是普通消息,还有是IdelHandler 消息;next方法主要流程如图
以上流程基本上涵括了处理以上三种消息的时机;我们再来简答概括下,可分为3步
1:检查是否有消息屏障
2:检查是否有满足条件的消息
3:如果1,2步都没有,则试着处理是否有IdelHandler