一图胜千言
read the fuck source code
分别看一下三个核心类Handler,MessageQueue,Looper都做了什么
首先从Handler的sendMessage()方法开始
经过一系列的调用,最后通过MessageQueue的enqueueMessage方法,将Message插入消息队列.
这里的Message其实是一个链表结构.
看一下MessageQueue是怎么处理添加进来的Message的
第一个if里面,如果当前列表是空的,直接插入.
否则,遍历当前的链表直到找到时间大于当前message的时间的message, 或者当next为null,遍历到了尾节点,插入进去.
这里注意:MessageQueue里面的消息,是根据时间排好序的,所以Looper中依次轮询即可.
那么什么时候取消息呢?
当我们的应用进程启动的时候ActivityThread的main方法就开启了MainLooper的轮询,这方面的知识可以看下Acitviy启动流程这篇文章.
直接看Looper.loop方法
就是开启for(;;)循环,循环调用MessageQueue.next方法获取Message对象
如果返回为空代表应用退出了,调用Looper.quit()方法.
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
当子线程的Looper获取不到Message的时候,Looper就退出循环.
如果消息不为空调用msg.target.dispatchMessage(msg)分发给Handler执行,这里的msg.target就是发送Message的Handler.
接着看一下MessageQueue.next方法的实现
这里面有一个很重要的方法
nativePollOnce(ptr, nextPollTimeoutMillis);
//休眠到一定时间自动唤醒
//或者等到新消息到来主动唤醒nativeWake(mPtr);
其他相关问题
Handler Looper MessageQueue Message的关系
一个线程对应一个Looper,一个Looper对应一个MessageQueue
为什么建议使用Message.obtain方法创建Message
使用Message.obtain方法创建对象的好处是可以利用对象缓存池sPool
Message.obtain源码:
synchronized (*sPoolSync*) {
if (*sPool* != null) {
Message m = *sPool*;
*sPool* = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
*sPoolSize*--;
return m;
}
}
return new Message();
}
Looper.prepare()源码
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));
}
prepare就是将Looper对象放到TheadLocal中
ThreadLocal
当创建一个ThreadLocal变量时,访问这个变量的每个线程都有这个变量的一个本地副本。 当多个线程操作这个变量时,实际上就是操作自己本地内存里面的变量,从而避免了线程安全问题。 这个变量副本存放在ThreadLocalMap中。对应关系如下
ThreadLocalMap存放的就是以ThreadLocal为key,以ThreadLocal的initialValue为Value的合集. initialValue的值,默认为null
举个例子:
public class ThreadLocaDemo {
private static ThreadLocal<String> localVar = new ThreadLocal<String>();
static void print(String str) {
//打印当前线程中本地内存中本地变量的值
System.out.println(str + " :" + localVar.get());
//清除本地内存中的本地变量
localVar.remove();
}
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
public void run() {
ThreadLocaDemo.localVar.set("local_A");
print("A");
//打印本地变量
System.out.println("after remove : " + localVar.get());
}
},"A").start();
Thread.sleep(1000);
new Thread(new Runnable() {
public void run() {
ThreadLocaDemo.localVar.set("local_B");
print("B");
System.out.println("after remove : " + localVar.get());
}
},"B").start();
}
}
A :local_A
after remove : null
B :local_B
after remove : null
Looper的prepare方法就是将Looper自己set到ThreadLocal中去,这样就保证在当前线程中只有一个Looper对象
看一下set方法做了什么
Linux的epoll机制,阻塞式休眠
原理IO多路复用
epoll_create:建立一个epoll对象
epoll_ctl:添加socekt
epoll_wait
nativeWake(mPtr);
面试题:
1.如果发送同一个消息会怎样
源码中会抛出异常
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.");
}
2.如果频繁发送Message会怎样
频繁的创建Message会造成内存抖动,甚至OOM
造成卡顿
3.如何防止Handler内存泄漏
handler.removeCallbacksAndMessage(null)
静态内部类+弱引用
4.内存屏障是什么
当MessageQueue轮询到内存屏障类型的消息,会优先处理所有的异步任务,ui绘制的时候就是通过添加消息屏障,保证绘制流程.