对于Android消息机制源码分析已经烂大街了,之前跟网上大佬走了一遍,还记录了一下(《》)。
我们知道消息机制涉及如下几个类
涉及文件
frameworks\base\core\java\android\os\Looper.java frameworks\base\core\java\android\os\MessageQueue.java frameworks\base\core\java\android\os\Message.java frameworks\base\core\java\android\os\Handler.java
正文
Android应用开发时,如果想在子线程里创建自己的Handler消息处理,需要如下步骤:
private class MyThread extends Thread { @Override public void run() { super.run(); //1. Looper准备 Looper.prepare(); //2. Handler的创建 # 方式一 mThreadHandler = new Handler(Looper.myLooper(), new Handler.Callback() { @Override public boolean handleMessage(Message msg) { return false; } }); # 方式 mThreadHandler = new Handler(Looper.myLooper()){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; // PS:此时(2和3之间)也可以让Handler发现消息,但真正处理是在Looper进入循环之后。 //3.进入死循环,如果不是退出,此句之后的代码不会执行。 Looper.loop(); } }
#线程启动,启动之后mThreadHandler可以发送消息 MyThread mMyThread = new MyThread(); mMyThread.start();
Looper.java
先看Looper的构造函数,然看看常用的方法。
Looper的构造函数
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
带一个参数quitAllowed,最终是传入了MessageQueue(),表示是否可以退出。
ActivityThread中的主线程Looper,是不允许退出的(quitAllowed=false);而对于其他子线程创建的,默认是可以退出的。
准备
prepare()
不是应用主线程时调用这个,主要调用prepare(true),true表示运行退出。
public static void prepare() { prepare(true); }
调用的另外一个prepare(boolean quitAllowed),传入的参数是true。
prepareMainLooper()
应用主线程时调用这个,主要调用prepare(false),false表示不允许退出,也就是调用quit()后会抛出异常。
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { //判断是否之前初始化过,这是确保只能初始化一次 if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
prepare(boolean)
private static void prepare(boolean quitAllowed) { //从线程本地变量中获取looper if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } //保存Looper对象到线程本地变量中 sThreadLocal.set(new Looper(quitAllowed)); }
ThreadLocal是线程的局部变量,每个线程中的值都是独立存在、互不影响。因此,一个线程内的Looper都是独一份的!
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
循环
loop()
主要是获取到Looper中创建的MessageQueue对象,然后进入死循环,不断的从MessageQueue对象中拿取消息。如果没有消息就阻塞在queue.next()。
public static void loop() { //获取当前的Looper,通过sThreadLocal保存的 final Looper me = myLooper(); //获取Looper中的消息队列 final MessageQueue queue = me.mQueue; //略 //进入死循环 for (;;) { //从消息队列中拿去消息,存在阻塞 Message msg = queue.next(); //一般情况下不会为null的,具体看消息队列中的next() if (msg == null) { return; } //略 try { //msg.target就是Handler msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { } //回收消息 msg.recycleUnchecked(); } }
退出
quit()
public void quit() { //调用消息队列的quit mQueue.quit(false); }
quitSafely()
public void quitSafely() { mQueue.quit(true); }
真正干活的是MessageQueue,只不过传入的值不一样,一个false一个true。至于有啥区别,看后面MessageQueue的介绍。
获取Looper
getMainLooper()
获取主线程的sMainLooper,这个也是在初始化是通过sMainLooper=myLooper()获取的。
public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } }
myLooper()
从sThreadLocal中获取Looper
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
获取消息队列
getQueue()
返回消息队列
public @NonNull MessageQueue getQueue() { return mQueue; }
上面方法是Looper中比较常用的。
Handler.java
Handler消息的分发者和处理者,主要涉及消息的分发,消息的移除和消息的处理。
Handler的构造函数好多个,这里只是介绍常用的几个。
# 下面两个比较常用 public Handler(Looper looper) { this(looper, null, false); } public Handler(Looper looper, Callback callback) { this(looper, callback, false); } # 但最终都进入下面这种构造函数 public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
也就是这里传入了Looper,并且让Handler获取Looper中对的MessageQueue,后面消息的分发需要通过这个的。
如果传入了mCallback,那就是消息的处理可能是通过这个接口处理。
消息发送
Handler是消息的发送者,里面提供了很多丰富的接口。有postxx()和sendxx()两种。
下面就简单的附上常用的方法
post()
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
postDelayed()
public final boolean postDelayed(Runnable r, long delayMillis){
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
上面post中需要注意的是,这里传入的是Runnable,因此需要通过getPostMessage()转换一下成为Message。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
//对callback赋值,消息处理时会对此进行判断
m.callback = r;
return m;
}
sendEmptyMessage()
public final boolean sendEmptyMessage(int what){
return sendEmptyMessageDelayed(what, 0);
}
sendEmptyMessageDelayed()
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
sendMessageDelayed()
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
等等好几个send,其实最终都是通过enqueueMessage()分发。
enqueueMessage()
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//传入当前发送消息的Handler
msg.target = this;
//Handler初始化时传入的,默认是false
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//最终进入消息队列中
return queue.enqueueMessage(msg, uptimeMillis);
}
消息移除
消息移除就是把定时的消息从消息队列中移除掉。
下面几个是比较常用的方法
removeCallbacks()
public final void removeCallbacks(Runnable r){
mQueue.removeMessages(this, r, null);
}
removeMessages()
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
}
removeCallbacksAndMessages()
public final void removeCallbacksAndMessages(Object token) {
mQueue.removeCallbacksAndMessages(this, token);
}
最终消息的移除都是进入了消息队列中。
消息处理
上面Looper中介绍到Looper.loop()中会一直循环,如果有消息就会进入消息的分发
msg.target.dispatchMessage(msg);
这里的msg.target就是Handler发送消息是带上的this,也就是发送者Handler。
dispatchMessage()
public void dispatchMessage(Message msg) {
//callback就是Runnable
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果Handler中创建带入了Callback,就走Callback的回调。
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//默认走这,也就是创建是需要重写
handleMessage(msg);
}
}
msg.callback的赋值一般是post时封装Runnable的,可以看上面getPostMessage()方法。
private static void handleCallback(Message message) {
//也就是调用run()方法
message.callback.run();
}
消息存在
判断是否有某个消息,这个也很常见。
不过最终处理依旧是消息队列。看后面介绍。
hasMessages()
public final boolean hasMessages(int what) {
return mQueue.hasMessages(this, what, null);
}
hasCallbacks()
public final boolean hasCallbacks(Runnable r) {
return mQueue.hasMessages(this, r, null);
}
Message.java
Message也就是我们常说的信使,用于封装传递的信息。
消息中存储了很多有用的信息,比如我们常用的
what 消息类型code target 记录Handler发送的 when 什么时候处理 obj 用于传输对象 arg1 存储int类型值1 arg2 存储int类型值2 callback Runnable类型
创建信息的方式主要有
# 1 Message message = new Message(); # 2 Message message = mHandler.obtainMessage(); Message message = obtainMessage(int what)
一般推荐后面的方式,这里会重复利用创建的Message,也就不用每次都new一个新的。
我们看看为啥推荐mHandler.obtainMessage()。
# Handler.java
public final Message obtainMessage(){
return Message.obtain(this);
}
我们看看obtain(this)是怎么处理的。
obtain(Handler)
# Message.java
public static Message obtain(Handler h) {
Message m = obtain();
//target是Handler
m.target = h;
return m;
}
obtain()
private static Message sPool;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
//获取下一个Message,感觉像链表
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
上面就判断sPool是否为null,是就创建新的Message,否则就返回之前创建过的,同时清除使用过的flags。
用上面方式,不用每次都创建Message
至于回收,是在recycleUnchecked()中处理的。
recycleUnchecked()
回收使用过的Message
void recycleUnchecked() {
//设置FLAG_IN_USE标签
flags = FLAG_IN_USE;
//清除Message中所以信息
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
//如果没有达到MAX_POOL_SIZE(50)限制,就保存。
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;//有点链表的意思
sPool = this;
sPoolSize++;
}
}
}
MessageQueue.java
MessageQueue消息队列,消息存储,消息获取,以及推出消息循环都在这。
退出
上面说过,退出Looper调用的是MessageQueue.quit()。
void quit(boolean safe) {
//先判断是否可以退出,上面Looper.prepar()中传入的
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
//判断是否正在退出。防止多次进入
if (mQuitting) {
return;
}
//设置退出flag
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
//nativeWake用于唤醒功能
nativeWake(mPtr);
}
}
PS : mQuitting为true,next会进行判断然后是否退出
由于safe的值不同,退出会进入两个不一样的循环。
safe为true时,清空延迟消息(安全)
safe为false时,清空所有消息
removeAllFutureMessagesLocked()
为啥说这个是安全的?因为这里有条件判断,如果正在处理的消息,会等待其处理完。
(1)如果Message还没处理(p.when > now),就全部移除,调用的是removeAllMessagesLocked()
(2)如果Message正在处理(p.when == now),就等待其处理完,然后定位到消息队列中还没处理的消息并移除。
具体看下面,有注释
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
//查看当前消息p是否在处理中
if (p.when > now) {
//没有处理,清空所有消息
removeAllMessagesLocked();
} else {
//表示p消息正在处理,就查询其他未处理的消息
Message n;
//定位到到消息队列中还没处理的消息
for (;;) {
//获取下一个消息
n = p.next;
if (n == null) {
//如果下一个消息为null,表示后面没有了,直接返回
return;
}
//如果下一个还没开始处理,退出循环,
if (n.when > now) {
break;
}
//如果上面下个消息也在处理,继续查询下一个,p赋值为n,循环再次获取n.next.
p = n;
}
p.next = null;
do {
//遍历还没处理的消息并清除
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
removeAllMessagesLocked()
这里不安全,是因为不管三七二一,消息队列中的所有消息都移除。有点暴力!
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
消息存储
把消息存储到消息队列,上面Handler中介绍过posetxx()和sendxx(),最终都进入了消息队列中的enqueueMessage()。
enqueueMessage()
这里主要如下功能:
根据需求把Message放于队列第一个
如果没有需求就按照时间排序
根据needWake值看是否需要唤醒,通知消息分发
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");
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
//队列中第一个消息,可能为null
Message p = mMessages;
boolean needWake;
//如果p为nul,表示队列中没有消息
// when = 0 表示当前消息需要放在消息队列前。
// when < p.when 表示当前消息处理时间比上一个消息p更早
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; // invariant: p == prev.next
prev.next = msg;
}
if (needWake) {
//根据是否需要换线,然后通过nativeWake去通知唤醒
nativeWake(mPtr);
}
}
return true;
}
消息获取
上面介绍过,Looper进入loop()循环是通过消息队列拿去消息的。
Message msg = queue.next();
next()
next()就是从消息队列中获取消息。
如果没有消息,就阻塞
如果有消息(消息队列的第一个,下面也是一样),但msg.target=null就查询异步消息
如果有消息,但还没到处理时间,就等待
如果有消息,到了处理时间,就返回需要处理的消息
等
这里部分还不太懂。
Message next() {
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();
}
//阻塞操作,等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//当消息的Handler(msg.target)为空时,则查询异步消息
if (msg != null && msg.target == 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;
//msg.target == null时才会对这个赋值,一般为null
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;//指向下一个消息
}
msg.next = null;
//设置消息的使用状态
msg.markInUse();
return msg;
}
} else {
//没有消息
nextPollTimeoutMillis = -1;
}
//消息正在退出,返回null
if (mQuitting) {
dispose();
return null;
}
//消息队列的第一个消息时(pendingIdleHandlerCount默认-1)
//并且(1)当消息队列为空(2)消息还没到处理时间
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
//没有idle handlers 需要运行,则循环并等待。
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
//会运行idle handlers,执行完成,重置pendingIdleHandlerCount
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
//去掉handler的引用
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
//idle时执行的方法
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
//重置idle handler个数为0,以保证不会再次重复运行
pendingIdleHandlerCount = 0;
//当调用一个空闲handler时,一个新message能够被分发,因此无需等待可以直接查询pending message.
nextPollTimeoutMillis = 0;
}
}
消息移除
就是Handler中调用的方法,主要有如下
removeMessages()
removeMessages()这个方法有两个,只是传入的参数不一样,这里只介绍一个。
void removeMessages(Handler h, Runnable r, Object object) {
if (h == null || r == null) {
return;
}
synchronized (this) {
//mMessages是消息头,也是消息队列中的第一个消息
Message p = mMessages;
/*
这里写法有点怪
1. p != null 必要条件
2. p.target == h 必要条件
3. p.callback == r 必要条件
4. (object == null || p.obj == object) 必要条件
也就是上面4个条件都为true,才可以进入while中的循环。要不然一次都不执行。
写成这样
设计者可能考虑第一个第二个第三个消息都满足条件,直到不满足条件的消息就停止。
*/
while (p != null && p.target == h && p.callback == r
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();//清空并回收
p = n;
}
while (p != null) {
Message n = p.next;
//遍历后面符合条件的,如果符合就移除
if (n != null) {
if (n.target == h && n.callback == r
&& (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
removeCallbacksAndMessages()
哈哈,这个很熟悉吧,网上推荐Handler在Activity退出时通过这个移除所有消息,防止内存泄漏。
mHandler.removeCallbacksAndMessages(null);
今天我们就看一下其中是怎么处理的。
void removeCallbacksAndMessages(Handler h, Object object) {
if (h == null) {
return;
}
synchronized (this) {
Message p = mMessages;
/**
跟removeMessages()一样,也是先从头消息开始。
1. p != null 必要条件
2. p.target == h 必要条件,只移除当前Handler
3. (object == null || p.obj == object) 必要条件
也就是上面3个条件都为true,才可以进入while中的循环。要不然一次都不执行。
写成这样
设计者可能考虑第一个第二个第三个消息都满足条件,直到不满足条件的消息就停止。
*/
while (p != null && p.target == h
&& (object == null || p.obj == object)) {
Message n = p.next;
mMessages = n;
p.recycleUnchecked();
p = n;
}
while (p != null) {
//遍历后面符合条件的,如果符合就移除
Message n = p.next;
if (n != null) {
if (n.target == h && (object == null || n.obj == object)) {
Message nn = n.next;
n.recycleUnchecked();
p.next = nn;
continue;
}
}
p = n;
}
}
}
消息存在
这个也有两个,只是参数不一样。
原理都一样,都是遍历消息队列,根据条件查询。这里只介绍一个。
hasMessages()
boolean hasMessages(Handler h, int what, Object object) {
if (h == null) {
return false;
}
synchronized (this) {
Message p = mMessages;
while (p != null) {
//对比查询条件
if (p.target == h && p.what == what && (object == null || p.obj == object)) {
return true;
}
p = p.next;
}
return false;
}
}
参考文章
《》
《》
《》
《》
