深入源码分析Handler 消息机制 、Looper、MessageQueue 消息同步屏障、IdleHandler、Message 复用
Handler 线程通信 基本使用
在Android 中Handler大多数都是在子线程中发送消息,到主线程中更新UI,下面是基本使用
// 步骤1:在主线程中 通过匿名内部类 创建Handler类对象 mHandler = new Handler(){ // 通过复写handlerMessage(),处理其他线程发来的消息 @Override public void handleMessage(Message msg) { // 处理消息 } }; // 采用继承Thread类实现多线程 new Thread() { @Override public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } // 步骤3:创建所需的消息对象 Message msg = Message.obtain(); msg.what = 1; // 消息标识 msg.obj = "A"; // 消息内存存放 // 步骤4:在工作线程中 通过Handler发送消息到消息队列中 mHandler.sendMessage(msg); } }.start();
源码分析
再开始流程分析前,先来介绍一下,比较关键的几个类:
- Handler 用于给开发者,实现消息跨线程的发送和处理
- Message 发送的消息
- MessageQueue 消息队列,Handler 发送的消息都会进入这个队列
- Looper 消息处理循环器
Handler的创建
public Handler(Callback callback, boolean async) { //检测当前创建的handler是否可能引起内存泄漏 if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); //handler 是匿名类或成员类或本地类 并且不是静态的,就可能出现内存泄漏 if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } }//获取当前的线程的looper mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } //获取looper的队列 mQueue = mLooper.mQueue; //设置消息的回调,发送消息后,如果设置callback,就会触发回调 mCallback = callback; //true:发送的是异步消息 mAsynchronous = async; }
Java 内部类、成员类、局部类、匿名类等
Looper 的创建
看到上面mLooper = Looper.myLooper();
里面都干了什么呢
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
看到是从ThreadLocal类型的变量中,获取一个Looper。因此不同的线程中会创建不同的Looper实例。
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)); } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
也就是说,在使用Handler的时候,要先调用静态函数Looper.prepare()
创建属于当前线程的Looper,在调用Looper.loop()
来开启消息循环,这个循环是个死循环,等待Handler发送过来的消息。
小伙伴可能有疑问为什么,我平时更新UI的时候,没有调用这个,直接创建Handler实例,发送消息就可以了呢?
是因为Android的入口mani()函数中,已经调用过了。而你创建Handler实例是在主线程,也就不能也不需要再创建Looper,否则会报错。
Message 相关
先来看几个重要的成员变量:
- what 消息标识
- arg1,arg2 传递的int参数
- data 可以传送Bundle 数据
- target 发送这个消息的Handler
- next 下一个Message
- flags 是否在使用
Message.obtain()
来获得一个Message的实例,源码写的很高效,这里对消息进行了复用。
public static Message obtain() { //同步操作,避免多个线程,同时对消息池进行操作 //sPool 存放的是,Message 消息队列的队首消息 synchronized (sPoolSync) { if (sPool != null) { //获得队首的Message Message m = sPool; //更新sPool 为下一个Message sPool = m.next; //当前的Message next置空 m.next = null; //被使用标志置0 m.flags = 0; // clear in-use flag sPoolSize--; return m; } } //如果sPool 为空,创建一个实例 return new Message(); }
那么sPool 的队列信息,是在Message 被回收的加入的,具体有两种情况
- 在Looper 中消息被处理后,会调用
msg.recycleUnchecked();
回收Message - 在把消息添加到MessageQueue时enqueueMessage,如果是正在退出,会调用
msg.recycle();
也会回收Message
下面看一下回收的过程
public void recycle() { //检查是否正在使用 if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked(); } void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. //Message的成员变量全部情况 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) { //如果消息队列数量小于MAX_POOL_SIZE,就继续添加到队列中 if (sPoolSize < MAX_POOL_SIZE) { //当前消息(即将被回收)的next指向当前队首 next = sPool; //把sPool指向当前消息,就把消息回收到队列中了 sPool = this; sPoolSize++; } } }
消息流程
我们从发送消息mHandler.sendMessage(msg);
来看,最终是怎么消息的线程通信,以及内部的一些原理
除了postAtFrontOfQueue
发送消息外,其他的消息发送postAtTime
,postDelayed
,sendMessageDelayed
,sendMessage
最终会把消息发送的时间确定下来,调用 sendMessageAtTime
来处理消息。
Handler类 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); }
Handler类 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
上面两个函数,很简单,最终去调用queue.enqueueMessage
去把消息插入队列中
MessageQueue类 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) { //Loop.quit() 调用了 MessageQueue quit(),会设置mQuitting = true,表示正在退出 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) { // New head, wake up the event queue if blocked. //p 为空,队列中没有消息,或者延迟时间为0,或者延迟时间小于p的延迟时间(也就是需要在p之前执行) //就把这个消息放在p前面,也就是队首(因为p为下一个要处理的消息) msg.next = p; //更新mMessages (下一个要处理的消息) mMessages = msg; //是否需要唤醒,因为可能在MessageQueue 中获取message的时候,可能被阻塞 needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. // mBlocked 为阻塞。在取消息函数next()可知,当没有消息的时候,这个参数会置位true // p.target == null 下一个消息是同步屏障 ,就是优先处理异步消息,这个后面会详细分析 // msg 是异步消息。 //这三个消息同时为true ,才去唤醒线程 needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; //prev 表示队列第N个消息,p 表示队列第N+1个消息 if (p == null || when < p.when) { //p为空,说明已经是最后一个消息了 //when < p.when,说明需要在p之前执行 break; } if (needWake && p.isAsynchronous()) { //在msg插入到队列中,已有消息是异步的,那就遵从前面的线程状态,不进行唤醒 needWake = false; } } //下面两行代码后,prev 表示队列第N个消息,msg表示队列第N+1个消息,p表示队列第N+2个消息 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; }
之前的Loop.loop() 一直在循环,从MessageQueue中获取消息,下面省略的代码,是计算消息执行时间,和预期时间是否有差异,打印log,这里不进行分析。主要就是三个步骤:
Loop类 public static void loop() { 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; ... 省略代码 ... for (;;) { //1、取出MessageQueue 中的下一个消息 Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } ... 省略代码 ... try { //2、处理消息 msg.target.dispatchMessage(msg); ... 省略代码 ... } finally { ... 省略代码 ... } ... 省略代码 ...//3、回收消息 msg.recycleUnchecked(); } }
接下来,先分析 Message msg = queue.next(); // might block
看它是如何取出消息的,以及如何实现同步屏障(优先处理异步消息),以及IdleHandler的作用·
Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; }//第一次进来才会设置为-1 int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); }//会阻塞线程 时间为nextPollTimeoutMillis nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); Message prevMsg = null; //mMessages 是队列的头 Message msg = mMessages; if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. //找出下一个异步消息 ,也就是说屏蔽了同步消息。所以 msg.target == null 这样的消息在江湖上叫做 同步屏障的消息。可调用postSyncBarrier()来发出 do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } 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. //获取到一个新消息,才把阻塞标志设为false mBlocked = false; if (prevMsg != null) { //如果是同步屏障了,那么prevMsg !=null, 这里把msg 取出,调整链表指向 //队列的头 mMessages 没有变化 prevMsg.next = msg.next; } else { // 从队列的头取出消息 msg,所以需要更新mMessages mMessages = msg.next; } msg.next = null; if (DEBUG) Log.v(TAG, "Returning message: " + msg); msg.markInUse(); return msg; } } else { // No more messages. nextPollTimeoutMillis = -1; } // Process the quit message now that all pending messages have been handled. if (mQuitting) { dispose(); return null; } // If first time idle, then get the number of idlers to run. // Idle handles only run if the queue is empty or if the first message // in the queue (possibly a barrier) is due to be handled in the future. if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { //其实官方已经注释的很清楚,我再啰嗦一下 //第一次进来pendingIdleHandlerCount = -1 //没有需要处理的消息,或者消息要过一会再处理。 //这两个条件同时满足,会进入下面的for循环,也就是IdleHandler的调用 pendingIdleHandlerCount = mIdleHandlers.size(); } //如果后续的循环不会再执行IdleHandlers,因为下面最后代码会把pendingIdleHandlerCount 设置为0 if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. //如果没有要执行的IdleHandler或者是第二次循环,则设置为阻塞中,开始下一次循环 mBlocked = true; continue; }//创建mPendingIdleHandlers 数组,用来存放设置的IdleHandler if (mPendingIdleHandlers == null) { mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; } mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); } // Run the idle handlers. // We only ever reach this code block during the first iteration. 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 { //返回值,true = 下次循环需要继续执行 (也就是keep的意思) keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); }//下次不需要执行,则删除这个IdleHandler 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; } }
- 从开始获取消息到获取到一个可用的消息,这之间都是处于阻塞状态。
- 开启同步屏障后,会屏蔽同步消息,执行下一个异步消息。记得需要取消同步屏障,否则会阻塞在nativePollOnce()
- 如果消息队列为空,就会去执行所有IdleHandlers,只执行一次
那么我们先回到loop循环中,看下获取到消息后, msg.target.dispatchMessage(msg);
的处理
Handler类 public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
可以看到回调处理的优先级,Message 的回调 > Handler 的回调 > handleMessage
这里的handleMessage
就是基本使用种,重写的方法。
到这里流程就算走完了,再来看一下
1、同步屏障如何使用,源代码是什么样的,msg.target == null
的消息,要怎么发送呢?
2、IdleHandler 有什么用途呢?
同步屏障消息
开启同步屏障:
public int postSyncBarrier() { return postSyncBarrier(SystemClock.uptimeMillis()); } private int postSyncBarrier(long when) { // Enqueue a new sync barrier token. // We don't need to wake the queue because the purpose of a barrier is to stall it. synchronized (this) { final int token = mNextBarrierToken++; 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) { //选择合适的位置,准备插入msg prev = p; p = p.next; } } if (prev != null) { // invariant: p == prev.next //队列插在prev 和 p 中间 //这里没有设置mMessages ,是因为在消息处理中,会不断把下一个消息设置到mMessages msg.next = p; prev.next = msg; } else { //msg 插在队列头部 msg.next = p; mMessages = msg; } //清除同步屏障,参数要指定token return token; } }
一定要在不需要的时候,调用否则线程会被挂起
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; //这里的mMessages,不一定就是屏障的消息(即使屏障的消息,是无法消耗的。比如刚发送,还在处理其他消息的时候,就取消) Message p = mMessages; while (p != null && (p.target != null || p.arg1 != token)) { // 找到指定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) { //下面语句,把p移除队列 prev.next = p.next; needWake = false; } else { //p 是队首 mMessages = p.next; //已经是队尾或者下一个消息用户消息,就需要唤醒线程 needWake = mMessages == null || mMessages.target != null; } //回收消息 p.recycleUnchecked(); // If the loop is quitting then it is already awake. // We can assume mPtr != 0 when mQuitting is false. if (needWake && !mQuitting) { nativeWake(mPtr); } } }
IdleHandler
参考:你知道android的MessageQueue.IdleHandler吗?
它的用途可以查看上面这篇文章,大概意思就是:
想在界面绘制出来后做点什么,在onResume 执行是不可以的,可以 使用IdleHandler ,这个时候IdleHandler就发挥作用了,它是在looper里面message暂时执行完毕了就会回调,顾名思义嘛,Idle就是队列为空的意思,那么我们的onResume和measure, layout, draw都是一个个message的话,这个IdleHandler就提供了一个它们都执行完毕的回调了
至此就全部结束了,如果有哪里 写的不清楚的 ,可以交流交流
更多相关文章
- SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
- 使用RxAndroid处理异步任务
- Android消息队列原理
- Android使用ThreadLocal+PriorityQueue构建队列
- 活用Android线程间通信的Message机制
- Android可以在子线程更新UI吗
- RxJava 学习笔记(四)
- 【android】HandlerThread的使用及源码剖析
- android-----AsyncTask源码分析