学习的起因的是因为看面经,学习的结果是快乐~
消息机制,听着感觉很高端,实际上就是Handler、Looper、MessageQueue三人组(别问我Message去哪了)。
第一步Looper.prepare()
主线程在ActivityThread中调用Looper.prepareMainLooper()
private static void prepare(boolean quitAllowed) {
//参数表示是否可以退出,默认为true,只有主线程为false
if (sThreadLocal.get() != null) {
//Looper存放在ThreadLocal中,一个线程最多只能有一个
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
然后初始化一个Looper:
private Looper(boolean quitAllowed) {
//偷偷建立一个MessageQueue
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
第二步new Handler()
Handler handler = new Handler();
三兄弟初始化完成。
第三步Looper.loop()
主线程在ActivityThread中调用Looper.loop()
public static void loop() {
...
//死循环进行不停的遍历Message
for (;;) {
Message msg = queue.next();
if (msg == null) {//被中断就会返回null,队列为空只会阻塞不会返回null
return;
}
...
try {
//消息分发,target实际上是一个Handler
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
}
}
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//通常会走这
}
}
到这里,已经初始化完成并且已经启动了,剩下的就是往里面加入Message。
第四步sendMessage
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);//第二个参数表示延时时间
}
最后调到MessageQueue.enqueueMessage
boolean enqueueMessage(Message msg, long when) {//单链表形式存储
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {//是否已经中断退出
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();//标识已经被使用
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {//非延时消息
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {//延时消息,根据时间插入到对应位置
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;
prev.next = msg;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
nativeWake()唤起睡眠的Looper,Looper会一直循环,当为空了就会睡眠,当有新数据插入就会唤醒。
Other
如何中断
quitSafely和quit都调用的MessageQueue中的quit方法,把mQuitting设为true,quitSafely会清空延时消息,发送及时消息。quit会直接清空所有消息(所以才不安全。。)enqueueMessage中判断如果已经被mQuitting则不会再继续插入元素。
为什么主线程Looper.loop()不会ANR
loop()在主线程循环可以使main函数永不停止,维持app的正常运行,不然app就直接结束了。
ANR原理:底层收到事件(点击时间、ui更新),发了一个handler到MessageQueue,结果Queue半天都轮不到这个事件(其他事件太费时间或者loop()被卡住了)或者没有及时处理完,底层过了几秒等急了(真没耐心,就跟客户一样)就向AMS反应说超时,但实际上过一会儿还是Queue还是轮到这个事件并处理。
ANR:是某一个消息或者说对消息的处理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞主线程。
实际上所谓的ANR只是说在Looper处理消息的过程中单个消息处理事件太久导致后续的事件无法处理导致的,跟looper的外层循环无关,是内部事件的问题。
2018年9月5日12:57:02 添加:
以下是自己后来思考过程中遇到的疑惑:
1.为什么Message的处理在looper的创建线程?明明处理方法是handler的。
因为Handler所在线程只是定义方法的地方,而looper.loop()所在线程才是真正调用的地方: msg.target.dispatchMessage(msg);
2.B线程如何持有A线程的looper进而创建对应的handler?
线程作为内部类,能调用外部handler变量,或者某些直接获取主线程的looper进行handler构建。拥有同一个looper是handler传递消息的基础,A在那不停的轮询消息队列,B持有A的looper所以和A共用同一个MessageQueue,然后就B往A的MessageQueue里面添加消息,A收到消息进行处理,这就是handler线程通信原理。