消息处理机制
简单一句话:
Handler获取当前线程中的looper对象,looper用来从存放Message的MessageQueue中取出Message, 再有Handler进行Message的分发和处理.
来张动图:
应用开始
从整个应用开始来看,Looper先开始准备,然后调用loop()方法
loop()方法里面会一直循环,使得整个主线程一直在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
.... Process.setArgV0("<pre-initialized>"); // Lppper开始准备 Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); // attach方法其实在thread里面便会创建一个Binder线程 // 这也就是为什么死循环后程序还能运作的原因 thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { // 条件是false进不来 Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); // Loop 开始循环,主线程进入了死循环 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); ... |
由此而来,我们一般会有2个问题:
(1) Android中为什么主线程不会因为Looper.loop()里的死循环卡死?
(2) 没看见哪里有相关代码为这个死循环准备了一个新线程去运转?
知乎上的回答(十分赞):点这里
有了这个引入后,接下来将会从Looper,Handler,Message,MessageQuene逐个分析
Looper
一般一个线程就对应一个Looper
重要成员有:
1 2 3 4 5 6 7 8 9 |
// ThreadLocal使得每个Thread都有一个自己的副本 // 这样就可以保证每个线程中都有一个自己的Looper,不会被别的线程共享 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); // 主线程中的Looper private static Looper sMainLooper; // guarded by Looper.class // MessageQueue 消息队列用作保存和取出Message final MessageQueue mQueue; // 当前线程 final Thread mThread; |
从开始我们可以先从Looper.prepareMainLooper()方法下手
其实就是调用了Looper中的prepare方法,
1 2 3 4 5 6 7 8 9 10 11 |
// quitAllowed // 是否允许退出,这里主线程的时候是传入false, //最后会在MessageQuene中的quit方法中抛出异常,从而不让退出 private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } //sThreadLocal 就像一个Map,一个新建的Looper给放进去 //类似一个线程对应一个Looper sThreadLocal.set(new Looper(quitAllowed)); } |
这里就到了Looper的构造方法:
1 2 3 4 5 6 |
// 这里把之前的quitAllowed给传进了MessageQueue; // 并且mThread是当前的线程 private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } |
接下来就是核心方法loop():
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
public static void loop() { // 获取loop和queue final Looper me = myLooper(); if (me == null) { //没有消息,退出循环,程序退出 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; //主要是为了确保是否还是当前进程 Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { // 这里就是死循环了 // 取出msg,如果为空,return掉 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } ... // target 就是 Handler,这里就会调用Handler的dispatchMessage方法发送消息 try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ... // 将Message回收到消息池,下次要用的时候不需要重新创建,obtain()就可以了。 msg.recycleUnchecked(); } } |
这里有个问题,主线程是UI线程和用户交互的线程,优先级应该很高,主线程的死循环一直运行是不是会特别消耗CPU资源吗?App进程的其他线程怎么办?
关键就是queue.next()方法里,如果此时没有消息,就会阻塞在这个方法里面。这时候主线程会释放CPU资源进入休眠状态,直到有下个消息进来时候就会唤醒主线程
.2 版本以前,这套机制是用我们熟悉的线程的wait和notify 来实现的,之后的版本涉及到Linux pipe/epoll机制,通过往pipe管道写端写入数据来唤醒主线程工作。原理类似于I/O,读写是堵塞的,不占用CPU资源。
MessageQuene
先看下构造方法:
1 2 3 4 5 6 7 |
MessageQueue(boolean quitAllowed) { // 之前的是否允许退出 mQuitAllowed = quitAllowed; // 可以理解为当前队列是否还有没效,0就是没,大于0就是有 // 后面的dispose方法就是让其无效,从而退出程序 mPtr = nativeInit(); } |
继续刚才,进入到queue.next()方法中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
Message next() { final long ptr = mPtr; if (ptr == 0) { // 队列无效,返回空退出程序 return null; } int pendingIdleHandlerCount = -1; int nextPollTimeoutMillis = 0; // 又来一个死循环了 for (;;) { ... // 该方法会阻塞队列 // 1.没消息的时候 // 2.nextPollTimeoutMillis>0,而且等待时间还没到 nativePollOnce(ptr, nextPollTimeoutMillis); // 返回继续执行 synchronized (this) { final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; // 如果mMessages当前消息不为空,handler不为空,也不是异步消息, // 把当前消息返回 if (msg != null && msg.target == null) { // 否则一直查找下去,直到是一条target不为空也不是异步的消息 // 如果是异步消息,则跳出循环 do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } // 如果mMessages不为空 if (msg != null) { if (now < msg.when) { // 时间还没到,要等下次,经常用的postDelay, 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 { // 没有消息 ,重新把时间变成-1 nextPollTimeoutMillis = -1; } } } |
这里就把queue.next()给分析完了,那问题是mMessages又是如何得到呢?
这里就是Handler发送消息的时候使用了enqueueMessage方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
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) { 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; } // 消息标记为使用中 flags |= FLAG_IN_USE msg.markInUse(); msg.when = when; // 消息发送时间 Message p = mMessages; //当前消息 boolean needWake; //是否需要唤醒 // 当前没有消息需要执行或者当前的消息执行时间比msg要大,说明,msg应该第一个执行 if (p == null || when == 0 || when < p.when) { // 当前mMessages变为要msg msg.next = p; mMessages = msg; // mBlocked 是否正在阻塞中,如果是,需要唤醒 needWake = mBlocked; } else { // 如果消息是异步的,p又没有target,而且还在阻塞中,变为要唤醒 needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; // 在当前消息队列中查找是否有消息比这个msg应该提前执行的, // 如果有就不执行 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; } // 如果需要唤醒,则唤醒,next方法() if (needWake) { nativeWake(mPtr); } } return true; } |
Message
消息,类似一个Model一样,主要是一些信息
里面有几个重要参数
1 2 3 4 5 6 |
// 锁对象 private static final Object sPoolSync = new Object(); // 消息池,就像一个链表一样,指向当前的消息 private static Message sPool; // 消息池的大小 private static int sPoolSize = 0; |
主要方法:obtain(),这里也就对应了之前为什么在Loopr最后调用msg.recycleUnchecked();
因为Message类可以看成是个单例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 没什么好说的,就是从消息池中获取一个消息,如果没有,就new一个 public static 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(); } |
当前消息变为新的消息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public static Message obtain(Message orig) { Message m = obtain(); m.what = orig.what; m.arg1 = orig.arg1; m.arg2 = orig.arg2; m.obj = orig.obj; m.replyTo = orig.replyTo; m.sendingUid = orig.sendingUid; if (orig.data != null) { m.data = new Bundle(orig.data); } m.target = orig.target; m.callback = orig.callback; return m; } |
Handler
Handler算是我们平时用的最多一个类
几个重要参数:
1 2 3 4 |
final Looper mLooper; // 所在的Looper,默认主线程 final MessageQueue mQueue; // 所在消息队列 final Callback mCallback; // callBack 最后回调 handleMessage 方法 final boolean mAsynchronous; // 是否异步 传给了消息的 mAsynchronous |
我们一般使用都会重写该handleMessage方法
1 2 3 4 5 |
/** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { } |
这里对于dispatchMessage方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public void dispatchMessage(Message msg) { // 消息的callback不为空 , 执行消息的 if (msg.callback != null) { handleCallback(msg); } else { // callback 不为空 执行 callback if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } // 最后才执行 handleMessage(msg); } } |
在看下平时经常用的几个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
1. public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } 2. public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); } 3. public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); } 4. public final boolean post(Runnable r) { // getPostMessage 也就是消息的构建一个callback = r 的消息 return sendMessageDelayed(getPostMessage(r), 0); } |
都会调用了sendMessageDelayed方法:
1 2 3 4 5 6 7 8 9 10 |
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); } |
最终调用了enqueueMessage方法,触发queue.enqueueMessage:
1 2 3 4 5 6 7 |
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; // 这里赋予target if (mAsynchronous) { //如果是异步的话 msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } |
结尾
至此,已经大致把整个消息机制基本流程分析了一遍,当然里面还有很多细节没有分析, 当然你也可以看看HandlerThread类,在研究下