消息机制可称为Handler机制,Android中的视图绘制,事件传递,四大组件的生命周期都离不开Handler机制,都是由Handler机制直接或间接的完成。java
下面是类图(第一次画类图,哈哈哈...)
缓存
先放一张流程图
bash
准备循环,主要是新建一个Looper并与本地线程进行绑定,具体源码以下less
public static void prepare() {
prepare(true);
}
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));
}
复制代码
有两个重载方法,默认传入的true,quitAllowed是否运行退出,主线程是不容许退出的。
内容比较简单,建立一个Looper并设置给mThreadLocal。那mThreadLocal是何方神圣呢?异步
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 复制代码
可看到它被static final修饰,进程中全局只有一个。泛型是Looper的ThreadLocal对象,看下ThreadLocal的注释async
* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
复制代码
提供线程的本地变量,每个线程都是独有的 再看下它的set方法ide
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
复制代码
获取当前线程,并获取线程中的threadLocals变量(该变量可看作一个Map集合),并将ThreadLocal自己为Key,set的值为value存入。由此看出神奇的ThreadLocal实际上经过Thread对象中的threadLocals变量保证了保存的变量在每一个线程的惟一性。oop
继续看下Looper的构造方法post
private Looper(boolean quitAllowed) {
//建立一个MessageQueue
mQueue = new MessageQueue(quitAllowed);
//保存当前线程对象
mThread = Thread.currentThread();
}
复制代码
开启循环,主要代码以下ui
public static void loop() {
//1.获取当前线程的Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//2.获取Looper中的MessageQueue对象
final MessageQueue queue = me.mQueue;
···
for (;;) {
//3.从MessageQueue中获取下一条要处理的消息 queue.next是阻塞方法
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
···
try {
//4.将获取的消息交于 msg.target处理 msg.target实际存储了发送该消息的Handler对象
msg.target.dispatchMessage(msg);
}
···
5.消息回收
msg.recycleUnchecked();
}
}
复制代码
该方法主要有四个步骤:
贴几个关键方法吧
public final boolean postAtTime(Runnable r, long uptimeMillis){
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(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(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
复制代码
能够看出全部的发送消息方法post和send最后都会调用sendMessageAtTime方法,而后调用queue.enqueueMessage(msg, uptimeMillis)方法,将Handler自身绑定到Message的target变量上
post方法有个不一样点是将Message.callback赋值为传入的Runnable对象,这块后面有用到。
MessageQueue的enqueueMessage方法
boolean enqueueMessage(Message msg, long when) {
//target不容许为空
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) {
msg.recycle();
return false;
}
//将msg标记为正在使用中
msg.markInUse();
//设置msg的触发时间
msg.when = when;
// 将P变量赋值为mMessages,mMessage是消息队列中的第一个消息
Message p = mMessages;
//是否须要唤醒
boolean needWake;
//若是消息队列中没有消息 或者 新消息的触发时间在消息队列的第一个消息以前时
//则新消息放入消息队列头部。若是当前正在阻塞,则须要唤醒 needWake = mBlocked
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 若是当前是阻塞状态 && 第一个消息是栅栏消息 && 新消息时异步消息时 须要唤醒 (栅栏后面会讲)
needWake = mBlocked && p.target == null && msg.isAsynchronous();
//根据when 从小到大的顺序 找到新消息在队列中的位置 并插入到消息队列中
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
//若是须要唤醒 && p是异步消息 则取消唤醒 这是由于新消息不是第一个要执行的异步消息,不用唤醒。
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
//调用本地方法唤醒
nativeWake(mPtr);
}
}
return true;
}
复制代码
从上面的源码能够看出,MessageQueue.enqueueMessage方法,是将新消息按照消息的触发时间when进行队列,找到新消息的具体位置,再根据当前的状态判断是否进行唤醒操做(实际上无论同步和异步消息,若是新消息是下一次要执行的消息时就会进行唤醒操做。)异步消息后面会讲,它和栅栏有一些关系
咱们从上面知道Looper.loop()方法会一直调用queue.next()取出消息进行处理,先放下queue.next()的源码
Message next() {
//mPtr和 native层的NavtiveMessageQueue 关联
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {//无限循环
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
//1.调用本地阻塞方法,nextPollTimeoutMillis为阻塞时长,-1 会一直阻塞,直到调用ativeWake(mPtr)唤醒
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
//2.取出下一次要执行的消息
Message msg = mMessages;//取出入队列的第一个元素
if (msg != null && msg.target == null) {
// 若是该消息是栅栏消息,则获取第一个异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
//3.根据下一条要执行的消息when 判断是否须要返回当前该条消息
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
//当前时间小于待触发消息的时间时,则赋值下一次的睡眠时间为两个时间差
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
//若是当前的时间大于或等于待触发消息的时间时,则将取出的消息标记为正在使用中。
// Got a message.
mBlocked = false;
if (prevMsg != null) {//当有栅栏时prevMsg != null 取出第一个异步消息后,保证队列的顺序
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// 若是没有消息,设置下一次超时时间为-1 会一直阻塞,直到调用ativeWake(mPtr)唤醒
nextPollTimeoutMillis = -1;
}
// 正在退出时会返回空
if (mQuitting) {
dispose();
return null;
}
//4.mIdleHandlers 空闲任务处理
//pendingIdleHandlerCount 表示将要处理的空闲任务数量 只有第一次执行循环时 pendingIdleHandlerCount<0
// mIdleHandlers 全部的空闲任务 调用一次next方法至多一次会进入该判断
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);
}
//若是执行到这里表示 有待执行的空闲任务 而且当前没有Message要处理
//执行待处理的任务
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);
}
}
}
//重置待执行空闲任务的数量
pendingIdleHandlerCount = 0;
//将阻塞时间设置为0 立刻进行下一次消息的取出
nextPollTimeoutMillis = 0;
}
}
复制代码
主要分4大块
** 空闲任务IdleHandle **
MessageQueue.java
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
public void removeIdleHandler(@NonNull IdleHandler handler) {
synchronized (this) {
mIdleHandlers.remove(handler);
}
public static interface IdleHandler {
boolean queueIdle();
}
复制代码
MessageQueue提供了两个关系IdleHandler的方法,一个是添加一个是删除
IdleHandler是一个接口,该接口只有一个queueIdle方法,返回一个boolean,若是返回false,该IdleHandler掉用过一次即被移除掉,不然每次调用next方法,进入空闲状态都会执行一次IdleHandler
从上面得知MessageQueue.next()方法返回要处理的message时,会在Looper.loop()方法的循环内部调用msg.target.dispatchMessage(msg),咱们知道msg.target其实是发送该msg的Handler,下面是handler.dispatchMessage(msg)源码
public void dispatchMessage(Message msg) {
//经过post... 方法发送的消息 msg.callback!=null
if (msg.callback != null) {
handleCallback(msg);
} else {
//mCallback能够经过Handler的构造方法传入 若是mCallback.handleMessage(msg)返回true表示该消息已消费处理
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//调用自身的handlerMessage 通常咱们自定义Handler时会重写该方法
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
复制代码
从上面的源码能够看出,日常咱们在使用自定义Handler时,若是重写了handlerMessage方法,执行的条件是,经过send方法发送消息,而且没有传入mCallback或者传入的mCallback的handlerMessage方法返回false
栅栏也可叫作同步栅栏,做用是拦截同步消息,放行异步消息。MessageQueue有下面几个关于栅栏的方法
MessageQueue.java
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
//加入一个同步栅栏
private int postSyncBarrier(long when) {
synchronized (this) {
//获取一个token mNextBarrierToken从0开始自增加
final int token = mNextBarrierToken++;
//建立一个栅栏消息 能够看出并无设置msg.target 其实msg是不是栅栏消息就是根据msg.target==null判断的
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
//根据栅栏消息的时间,找到栅栏消息在消息队列中的位置
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
//返回token 做为一个栅栏标记 用于取消栅栏
return token;
}
}
/**
*根据栅栏的token值移除栅栏消息
* @hide
*/
public void removeSyncBarrier(int token) {
// Remove a sync barrier token from the queue.
// If the queue is no longer stalled by a barrier then wake it.
synchronized (this) {
//找出栅栏消息和栅栏的前一个消息
Message prev = null;
Message p = mMessages;
while (p != null && (p.target != null || p.arg1 != token)) {
prev = p;
p = p.next;
}
if (p == null) {
throw new IllegalStateException("The specified message queue synchronization "
+ " barrier token has not been posted or has already been removed.");
}
final boolean needWake;
if (prev != null) {//若是栅栏不是第一个消息 则表示栅栏消息还没在执行到 因此不须要进行唤醒
prev.next = p.next;
needWake = false;
} else {//若是第一个是要移除的栅栏消息,则移除消息 移除后消息队列的第一个消息不是栅栏消息则进行唤醒
mMessages = p.next;
needWake = mMessages == null || mMessages.target != null;
}
//回收消息
p.recycleUnchecked();
if (needWake && !mQuitting) {
nativeWake(mPtr);
}
}
}
复制代码
同步栅栏功能的实现,再看上面的MessageQueue.next()方法
Message next() {
···
for (;;) {
···
synchronized (this) {
···
Message msg = mMessages;
//若是执行的该条消息是栅栏消息,则获取队列中的异步消息 msg.target == null即为同步栅栏消息
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
}
复制代码
能够看出同步栅栏的做用是 拦截栅栏后面同步消息,放行异步消息
放张图就明白了
红色的栅栏消息 蓝色的为同步消息 绿色的为异步消息
Message的构造方法上有下面这些注释
/*While the constructor of Message is public, the best way to get
* one of these is to call {@link #obtain Message.obtain()} or one of the
* {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
* them from a pool of recycled objects
*/
复制代码
当前咱们须要构造Message时,最好的方式是调用Message.obtain() 或者Handler#obtainMessage 方法的一个, 这样会复用消息池的对象
Handler#obtainMessage 方法最终调用的也是Message.obtain()方法,那么看下Message.obtain()方法的源码
private static Message sPool;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {//消息池不等于空
//取出消息池的第一个元素 而且将缓存数量-1 并返回
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
//若是消息池没有消息 则新建一个消息
return new Message();
}
复制代码
能够看出sPool便是消息池,而且是静态的,经过静态实现缓存效果。调用obtain方法时,会从消息池中取出第一个消息,若是消息为空,则新建一个Message
那么他在哪里赋值的呢? 咱们看Looper.loop()方法,在掉msg.target.dispatchMessage(msg)后面会调用一个 msg.recycleUnchecked() 方法,咱们看下这个方法
void recycleUnchecked() {
// 清除全部标记
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) { //若是消息池数量小于MAX_POOL_SIZE MAX_POOL_SIZE=50
//把消息自身做为消息池的第一个元素,消息池数量+1
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
复制代码
来张图说明一下,左边是MessageQueue 右边是sPool消息池
这张流程图包括Native层,后期有时间会写篇关于Native消息机制的文章。
(点击图片查看大图)