Android(安卓)进阶14:源码解读 Android(安卓)消息机制( Message MessageQueue Handler Looper)
- 不要心急,一点一点的进步才是最靠谱的。
读完本文你将了解:
-
- 前言
- Message
- 如何获取一个消息
- Message.obtain()
- 消息的回收利用
- MessageQueue
- MessageQueue 的属性
- 何时初始化
- 消息入队的过程
- 消息出队的过程
- Looper
- 线程相关 ThreadLocal
- 无限循环调度
- 如何停止
- Handler
- Handler 的属性
- 发送消息
- 处理消息
- 移除消息
- 主线程消息机制
- 总结
- Thanks
前言
本来我以为自己很了解 Handler,在印象中 Android 消息机制无非就是:
- Handler 给 MessageQueue 添加消息
- 然后 Looper 无限循环读取消息
- 再调用 Handler 处理消息
整体的流程有了,但是一直没有结合源码捋一捋。
直到有一天在使用 Handler 发送消息时遇到了一个问题:
This message is already in use.
这才去翻了翻源码,今天总结一下。
Android 消息机制主要涉及 4 个类:
Message
MessageQueue
Handler
Looper
我们依次结合源码分析一下。
Message
“消息机制”,其中要传递的就是 Message,官方对它的描述是:
包含任意类型的对象和描述信息,可以被发送给 Handler。
Message
的主要属性如下:
//用来标识一个消息,接收消息方可以根据它知道这个消息是做什么的 public int what; //如果你的消息要传递的数据是整型的,可以直接使用 arg1 和 arg2,而不需要使用构造一个 Bundle public int arg1; public int arg2; //一个任意类型的对象,在使用 Messenger 跨进程传递消息时,通常使用它传递给接收者 //在其他场景下我们一般使用 setData() 方法 public Object obj; //负责回复消息的 Messenger,有的场景下(比如接受者、发送者模型)需要使用它 public Messenger replyTo; //当前消息的标志,只在被 Messenger 传递消息时使用,其他情况下都是 -1 public int sendingUid = -1; //标识当前消息是否在被使用 //当一个消息入队时这个标志会被改变,在被重新获取后重置 //当一个消息已经在被使用时,二次入队或者回收会报错(这就是我前言中提到的错误原因) /*package*/static final int FLAG_IN_USE = 1 << 0; //标识当前 消息是否是异步的 /*package*/static final int FLAG_ASYNCHRONOUS = 1 << 1; //在 copyFrom 方法中要清除的标志 /*package*/static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE; /*package*/int flags; /*package*/long when; //很关键的数据部分 /*package*/Bundle data; //发送和处理消息关联的 Handler /*package*/Handler target; //消息的回调 /*package*/Runnable callback; //在有些场景下还会以链表的形式关联后一个消息 /*package*/Message next; //消息池 private static final Object sPoolSync = new Object(); private static Message sPool; //回收消息链表 private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; private static boolean gCheckRecycle = true;Pasted from: http://book2s.com/java/src/package/android/os/message.html#a940ebe121994bfb7f9629bca79beab6
可以看到,Message 中比较关键的属性有:
- 标识干什么的 what
- 两个简易的整型数据存储对象 arg1 arg2
- 存储复杂点的对象 Bundle
- 跨进程通信绑定数据的 object
- 与之关联的 Handler
- 还有一些标志位,和消息池什么的
如何获取一个消息
消息机制最开始肯定需要构建一个消息。
虽然 Message 的构造函数是 public
的,但是官方还是建议我们使用以下 2 种方式获取一个 Message 对象:
Message.obtain()
Handler.obtainMessage()
原因是这两个方法会从一个消息回收池里获取消息,而不是新建一个,这样可以节省内存。
Handler.obtainMessage()
也是调用的 Message.obtain()
:
public final Message obtainMessage() { return Message.obtain(this); }
去看一下 Message.obtain()
源码。
Message.obtain()
Message.obtain()
有 7 个重载方法,基本就是在获取一个 Message 对象的同时直接赋值:
我们选最复杂的一个看下源码:
public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) { Message m = obtain(); m.target = h; m.what = what; m.arg1 = arg1; m.arg2 = arg2; m.obj = obj; return m; }Pasted from: http://book2s.com/java/src/package/android/os/message.html#50de1a696587dcd5ae3c91eb63985af2
的确就是在 obtainI()
的基础上加了一些赋值。
Message.obtain()
源码如下:
public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // 获取一个复用的消息时,会重置标志位,之前它是 FLAG_IN_USE sPoolSize--; return m; } } return new Message(); }Pasted from: http://book2s.com/java/src/package/android/os/message.html#62dadbbac34056e7ad4fad36d757d6ef
可以看到,这个方法会获取前面提到的 private static Message sPool;
,如果 sPool
存在就从复用消息链表头部取一个消息,然后重置它的标志位;如果不存在复用消息链表就新建一个消息。
那什么时候往消息池中添加消息呢?
消息的回收利用
消息的回收在 Message.recyclerUnchecked()
方法:
void recycleUnchecked() { flags = FLAG_IN_USE; //当前消息被回收时,会标志为 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) { next = sPool; sPool = this; sPoolSize++; } } }
可以看到 recycleUnchecked
方法将当前 Message 标志为 FLAG_IN_USE,这样如果这个方法在被入队,就会报错。此外它还清除了其他数据,然后把这个消息加入了回收消息的链表中。
那 recycleUnchecked()
什么时候会被调用呢?
在 MessageQueue
和 Looper
中都有。
MessageQueue.removeMessages()
方法:
void removeMessages(Handler h, int what, Object object) { if (h == null) { return; } synchronized (this) { Message p = mMessages; // Remove all messages at front. while (p != null && p.target == h && p.what == what && (object == null || p.obj == object)) { Message n = p.next; mMessages = n; p.recycleUnchecked(); //这里调用了 p = n; } // Remove all messages after front. while (p != null) { Message n = p.next; if (n != null) { if (n.target == h && n.what == what && (object == null || n.obj == object)) { Message nn = n.next; n.recycleUnchecked(); //这里也调用了 p.next = nn; continue; } } p = n; } } }Pasted from: http://book2s.com/java/src/package/android/os/messagequeue.html#0dbba2b975383e8b440199e5d69c73de
Looper.loop()
方法:
public static void loop() { //... for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } //... msg.recycleUnchecked(); } }Pasted from: http://book2s.com/java/src/package/android/os/looper.html
至此我们可以看到,一个消息在被 Looper 处理时或者移出队列时会被标识为 FLAG_IN_USE
,然后会被加入回收的消息链表,这样我们调用 Message.obtain()
方法时就可以从回收的消息池中获取一个旧的消息,从而节约成本。
MessageQueue
MessageQueue
管理着一个 Message
的列表,Handlers
为它添加消息,Looper
从中取消息。
MessageQueue
的属性
// 队列是否可以退出 private final boolean mQuitAllowed; @SuppressWarnings("unused") private long mPtr; //底层使用的 code //消息链表的开头 Message mMessages; private final ArrayList mIdleHandlers = new ArrayList(); private SparseArray mFileDescriptorRecords; private IdleHandler[] mPendingIdleHandlers; private boolean mQuitting; //指出获取下一个消息的方法 next() 是否阻塞 private boolean mBlocked; // Barriers are indicated by messages with a null target whose arg1 field carries the token. //后一个屏障的 token private int mNextBarrierToken;
可以看到,MessageQueue
虽然叫“消息队列”,持有的其实是一个消息链表的节点。
何时初始化
MessageQueue
一般不直接访问,都是通过 Looper.myQueue()
方法获取一个消息队列。
消息队列在 Looper 的构造函数中初始化:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread();}MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit();}
MessageQueue 的构造函数中传入一个是否允许中途退出的标志,然后调用 Native 方法初始化。
这篇文章暂不研究 Native 层源码。
消息入队的过程
消息入队的方法是 enqueueMessage()
方法:
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { //这里要求消息必须跟 Handler 关联 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; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true;}
Message.enqueueMessage()
方法中会检查入队的消息是否在被使用,如果是的话会报错:
if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); }
现在我们清楚了,文章开头我遇到的:
This message is already in use.
是因为我二次使用了已经在使用的消息,在入队时 MessageQueue 检查发现后报的错。
所以每次调用 Handler.sendMessage()
时,都必须是 obtain()
或者 new 一个新的 Message 对象才行。
此外,如果消息队列已经退出,还添加消息入队就会报错。
消息出队的过程
消息出队的方法是 MessageQueue.next()
方法:
Message next() { //如果消息的 looper 退出,就退出这个方法 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 的这个方法 Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { //获取下一个消息 final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; //当前链表的头结点 if (msg != null && msg.target == null) { //如果消息没有 target,那它就是一个屏障,需要一直往后遍历找到第一个异步的消息 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; //取出当前消息,链表头结点后移一位 if (prevMsg != null) { prevMsg.next = msg.next; } else { mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); //标记这个消息在被使用 return msg; } } else { // 消息链表里没有消息了 nextPollTimeoutMillis = -1; } //如果收到退出的消息,并且所有等待处理的消息都处理完时,调用 Native 方法销毁队列 if (mQuitting) { dispose(); return null; } //有消息等待过段时间执行时,pendingIdleHandlerCount 增加 if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { mBlocked = true; continue; } if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } 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); } } } // Reset the idle handler count to 0 so we do not run them again. pendingIdleHandlerCount = 0; // While calling an idle handler, a new message could have been delivered // so go back and look again for a pending message without waiting. nextPollTimeoutMillis = 0; }}
可以看到,MessageQueue.next()
方法里有一个循环,在这个循环中遍历消息链表,找到下一个可以处理的、target 不为空的消息并且执行时间不在未来的消息,就返回,否则就继续往后找。
如果有阻塞(没有消息了或者只有 Delay 的消息),会把 mBlocked这个变量标记为 true,在下一个 Message 进队时会判断这个message 的位置,如果在队首就会调用 nativeWake() 方法唤醒线程!
其中看到个 IdleHandler
是什么呢?
public static interface IdleHandler { //当消息队列没有消息时会回调这个方法,阻塞等待有消息进入 //返回 true 的话表示唤醒阻塞的线程,false 表示移除 //如果消息队列中有消息等待在将来执行,也会调用这个方法 boolean queueIdle();}
根据源码和注释我们可以知道 IdleHandler
是一个线程阻塞时回调的接口。
MessageQueue 中提供了监听阻塞回调的注册和移除接口:
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); }}
当消息队列阻塞时,会回调这些监听阻塞的观察者,告诉他们:我有空了!来找我玩啊!
在 queueIdle()
中做的操作,它会在 handler 空闲时被调用,可以充分利用 handler,我们可以在代码里这样使用:
Handler handler = new Handler(); try { Field field = Looper.class.getDeclaredField("mQueue"); field.setAccessible(true); MessageQueue queue = (MessageQueue) field.get(handler.getLooper()); queue.addIdleHandler(new MessageQueue.IdleHandler() { @Override public boolean queueIdle() { //这里做一些操作 return true; } }); } catch (Exception e) { }
这里只简单介绍了 Java 层的 MessageQueue,关于 Native 层的可以看这篇文章:http://blog.csdn.net/innost/article/details/47317823
Looper
前面介绍了 Android 消息机制中消息和消息队列,有了传递的消息和存储的队列,接下来我们结合源码了解下进行调度的 Looper。
官方文档对 Looper 的介绍:
Looper 是用于运行一个线程中的消息的类。
Looper 的属性很简单:
// sThreadLocal.get() will return null unless you've called prepare().static final ThreadLocal sThreadLocal = new ThreadLocal();private static Looper sMainLooper; // 主线程中的 Looeprfinal MessageQueue mQueue; //与之管理的消息队列final Thread mThread; //所在的线程private Printer mLogging;private long mTraceTag;
线程中默认没有 Looper,我们需要调用 Looper.prepare()
方法为当前线程创建一个 Looper,然后就可以调用 loop()
方法调度消息。
样例代码如下:
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
线程相关 ThreadLocal
前面讲了在一个线程中需要调用 Looper.prepare()
方法创建一个 Looper:
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));}
可以看到,一个线程中只能有一个 Looper。
创建一个或者或许当前线程的 Looper 都通过 ThreadLocal
,我们来了解下它的主要源码。
ThreadLocal.get()
和 ThreadLocal.set()
源码:
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue();}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;}
ThreadLocalMap
中持有一个 Entry
的数组:
private Entry[] table;static class Entry extends WeakReference<ThreadLocal> { Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; }}
可以看到,ThreadLocal
先通过当前线程获取 ThreadLocalMap
,然后在 ThreadLocalMap
中保存 ThreadLocal
和 数据的关联。
也就是一个类似 Map
无限循环调度
在线程中创建一个 Looper
以后,就可以调用 Looper.loop()
循环处理消息了,看下它的源码:
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */public static void loop() { final Looper me = myLooper(); if (me == null) { //当前线程必须创建 Looper 才可以执行 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; //底层对 IPC 标识的处理,不用关心 Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { //无限循环模式 Message msg = queue.next(); //从消息队列中读取消息,可能会阻塞 if (msg == null) { //当消息队列中没有消息时就会返回,不过这只发生在 queue 退出的时候 return; } //... try { msg.target.dispatchMessage(msg); //调用消息关联的 Handler 处理消息 } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } //... msg.recycleUnchecked(); //标记这个消息被回收 }}
可以看到,Looper.loop()
也很简单,就是调用 MessageQueue.next()
方法取消息,如果没有消息的话会阻塞,直到有新的消息进入或者消息队列退出。
拿到消息后调用消息关联的 Handler 处理消息。
可以看到,Looper 并没有执行消息,真正执行消息的还是添加消息到队列中的那个 Handler,真应了那句:解铃还须系铃人啊!
如何停止
loop()
源码中的注释就提醒我们,开启循环调度消息后不要忘记调用 quit()
方法结束循环。
Looper.quit() 和 quitSafely ()
源码:
public void quit() { mQueue.quit(false);}public void quitSafely() { mQueue.quit(true);}
两种退出方式都调用的是 MessageQueue.quit(boolean)
方法:
void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); }}private void removeAllFutureMessagesLocked() { final long now = SystemClock.uptimeMillis(); Message p = mMessages; if (p != null) { if (p.when > now) { //`如果链表头部的消息执行时间在将来(也就是一时半会儿没有任务可执行) removeAllMessagesLocked(); //就直接强硬的全部回收了 } else { Message n; for (;;) { //否则找到那个执行时间大于现在的消息,把它后面的消息都回收了 n = p.next; if (n == null) { return; } if (n.when > now) { break; } p = n; } p.next = null; do { p = n; n = p.next; p.recycleUnchecked(); } while (n != null); } }}private void removeAllMessagesLocked() { Message p = mMessages; while (p != null) { //挨个遍历链表,把消息都回收了 Message n = p.next; p.recycleUnchecked(); p = n; } mMessages = null;}
可以看到 Looper 两种结束方式的区别:
quit()
:立即把消息链表中的所有消息都回收了,比较强硬
- 在停止后如果 Handler 还发送消息,会返回 false,表示入队失败
- 这个方法是不安全的,一般建议使用下面那个
quitSafely()
:比上面那个温柔一点
- 只会将还未执行的消息回收掉
- 在调用之后添加进入的消息不会被处理,Handler.sendMessage 也会返回 false
当消息队列被标记位退出状态时,它的 next()
方法会返回 null,于是 Looper.loop()
循环就结束了。
Handler
现在我们了解了消息队列和 Looper,接着就该介绍消息机制中最关键的角色 – Handler。
每一个 Handler 都和一个线程的 Looper 以及这个线程中的消息队列关联。
Handler 所做的就是 “线程切换”:
- 在子线程将 Message 或者 Runnable 发送到 MessageQueue 中
- 然后等待 Looper 调度这个消息后,再召唤 Handler 来处理消息
- 这时消息已经在创建 Handler 的线程了
这个“线程切换” 是怎么实现的呢?我们一步步揭晓。
Handler 的属性
Handler 的属性如下:
final Looper mLooper;final MessageQueue mQueue;final Callback mCallback;final boolean mAsynchronous;IMessenger mMessenger;
可以看到 Handler 的属性很简单,其中 mCallback 可以作为构造函数的参数用于新建 Handler。
public Handler(Callback callback) { this(callback, false);}public interface Callback { public boolean handleMessage(Message msg);}
我们平时创建 Handler 都是创建一个 Handler 的子类然后重写它的 handleMessage 方法,有了 Handler.Callback
接口我们可以用这种方式创建 Handler:
Handler mHandler = new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { //这里处理消息 return false; }});
最终效果和创建 Handler 子类一样,但是需要注意,这里创建了匿名内部类,还是会持有外部引用,导致内存泄漏。
发送消息
Handler 发送的主要有两种类型:
- Message
- Runnable
发送方法有 postXXX()
和 sendXXX()
两种,postXXX 发送的是 Runnable,调用的也是 sendXXX,而 Runnable 也会被转成 Message:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
所以我们直接看 sendXXX 方法:
public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0);}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);}
可以看到 Handler 发送消息最后还是调用了消息队列的 enqueueMessage()
方法。
而我们常用的使用 Handler.sendMessageDelayed()
发送延迟消息,最后其实是在入队时指定这个 msg.when,在 MessageQueue.next()
方法中,会对 msg.when > now 的消息设置延迟处理,具体实现是在 Native 层。
消息入队后,Looper 如果启动了就可以从队列里循环取消息,然后调用 msg.target.dispatchMessage(msg)``` 也就是
Handler.dispatchMessage()“ 方法处理消息。
处理消息
/** * Handle system messages here. */public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}private static void handleCallback(Message message) { message.callback.run();}public void handleMessage(Message msg) {}
可以看到,Handler 在处理消息时,会有三种情况:
- msg.callback 不为空
- 这在使用
Handler.postXXX(Runnable)
发送消息的时候会发生 - 这就直接调用 Runnable 的 run() 方法
- 这在使用
- mCallback 不为空
- 这在我们使用前面介绍的 Handler.Callback 为参数构造 Handler 时会发生
- 那就调用构造函数里传入的
handleMessage()
方法 - 如果返回 true,那就不往下走了
最后就调用
Handler.handleMessage()
方法- 这是一个空实现,需要我们在 Handler 子类里重写
至此 Handler 发送和处理消息的源码我们就了解了。
移除消息
最后再看一下如果移除消息。
由于发送时可以发送 Callback 和 Message,所以取消也有两种:
- removeCallbacks()
- removeMessages()
看一下源码发现调用的其实就是消息队列的出队方法:
public final void removeCallbacks(Runnable r){ mQueue.removeMessages(this, r, null);}public final void removeMessages(int what) { mQueue.removeMessages(this, what, null);}
主线程消息机制
我们知道,在主线程中创建 Handler 时不用 papare Looper,这是因为在主线程中系统默认创建了 Looper,它在不停地调度分发消息,因此四大组件的调度、我们的输入事件、绘制请求才能得到处理。
ActivityThread 就是我们说的主线程,而它的 main()
方法就是当主线程的入口:
public static void main(String[] args) { /... Process.setArgV0("" ); Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop();}
可以看到在主线程的 main()
方法中初始化了一个 Looper,然后调用了 Looper.loop()
方法开始调度消息。
发送和处理主线程中的消息的 Handler 叫做 H:
private class H extends Handler { public static final int LAUNCH_ACTIVITY = 100; public static final int PAUSE_ACTIVITY = 101; public static final int PAUSE_ACTIVITY_FINISHING= 102; public static final int STOP_ACTIVITY_SHOW = 103; public static final int STOP_ACTIVITY_HIDE = 104; public static final int SHOW_WINDOW = 105; public static final int HIDE_WINDOW = 106; public static final int RESUME_ACTIVITY = 107; public static final int SEND_RESULT = 108; public static final int DESTROY_ACTIVITY = 109; public static final int BIND_APPLICATION = 110; public static final int EXIT_APPLICATION = 111; public static final int NEW_INTENT = 112; public static final int RECEIVER = 113; public static final int CREATE_SERVICE = 114; public static final int SERVICE_ARGS = 115; public static final int STOP_SERVICE = 116; //... public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; case RELAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart"); ActivityClientRecord r = (ActivityClientRecord)msg.obj; handleRelaunchActivity(r); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; //... } if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what)); }
H 的代码很多,我们只节选一部分。从上述代码可以看到,H 内部定义了消息类型,然后根据消息的类型进行不同的处理。
主线程的消息机制如下:
- ActivityThread 通过 ApplicationThread 与 AMS 进行进程通信
- AMS 以进程通信的方式完成 ActviityThread 的请求后回调 ApplicationThread 的 Binder
- 然后 ApplicationThread 向 H 发送消息,H 将消息切换到主线程,然后进行处理
总结
这篇文章结合源码完整的看了一遍 Message MessageQueue Handler Looper,现在看着上面的图,可以自信地说我“熟悉 Android 消息机制”了哈哈。
结合 Android 性能优化:多线程 理解会更深一些!
Thanks
《Android 开发艺术探索》
http://book2s.com/java/src/package/android/os/handler.html#d0bdd75e69340e6c9e876ee32501beae
http://book2s.com/java/src/package/android/os/looper.html
http://book2s.com/java/src/package/android/os/message.html
更多相关文章
- 【Android(安卓)Training - 05】与其他Apps进行交互 [ Lesson 2
- Android(安卓)Handler消息机制原理及总结
- Android设置应用程序默认语言
- android handler 的removeMessages的使用
- android Handler机制源码详解
- JNI 数据类型转换
- Android中有关Handler的使用详解
- 开机自动运行程序【Android】
- Android异步任务和消息机制