前言
对于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; } }
参考文章
《》
《》
《》
《》