Handler机制是Android中相当经典的异步消息机制,本篇将从实用的角度来分析它。
Handler中有几个重要的类,由他们共同完成了这套消息机制的搭建,分别是:Handler、Looper、Message、MessageQueue。
下面来看一个使用案例:
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 class MainActivity : AppCompatActivity() { lateinit var handler: Handler override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Thread { Looper.prepare() handler = object : Handler() { override fun handleMessage(msg: Message) { super.handleMessage(msg) Log.d("TAG", msg.what.toString()) } } Looper.loop() Log.d("TAG", "Thread End.") }.start() btnSend.setOnClickListener { val message = Message.obtain(handler, 1) handler.sendMessage(message) } } }
运行后,我们通过 Log.d("TAG", msg.what.toString()) 成功打印了从主线程传递过来的数据。同时, Log.d("TAG", "Thread End.") 一直都没有执行。
问题1:为什么运行在子线程的Handler可以拿到主线程中发送的数据? 首先,我们看下Looper.prepare里面做了什么。
1 2 3 public static void prepare() { prepare(true); }
进入prepare方法。
1 2 3 4 5 6 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)); }
if 语句保证了每个线程只能存在一个Looper,sThreadLocal 在 Looper 中的定义如下。
1 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
可以看到 sThreadLocal 是一个静态变量,sThreadLocal.set(new Looper(quitAllowed));这行的作用是让 sThreadLocal 保存当前线程所对应的 Looper 。
回到 MainActivity ,之后我们重写了 Handler 的 handleMessage 方法,在方法中执行打印操作,那么 handleMessage 方法是什么时候被调用的呢?这个疑问先放着,我们之后再拐回来。
我们来看看 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 27 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;// mQueue 是什么赋值的?这个等下再分析,现在把它看成一个 MessageQueue 队列就行。 for (;;) { Message msg = queue.next(); // 这里会阻塞,直到 queue 中有消息加入。 if (msg == null) { return; } try { msg.target.dispatchMessage(msg); //发送消息到 handler 中。 } catch (Exception exception) { } finally { } msg.recycleUnchecked(); //回收消息 } }
这里只保留了主干代码。首先,拿到当前线程的 Looper 对象,不存在则抛出异常。然后,从 Looper 拿到它对应的 MessageQueue 对象,之后轮询 MessageQueue 的 next 方法,该方法是一个阻塞方法。拿到消息后通过 msg.target.dispatchMessage(msg) 将消息发送到 handler。最后释放消息。
下面我们看下 dispatchMessage 对消息是怎么处理。
1 2 3 4 5 6 7 8 9 10 11 12 public void dispatchMessage(@NonNull Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
先判断 msg.callback 是否为空,该例的场景为null,所以就到了 else 代码块中 ,由于这里 mCallback 为空,该消息最终传到了位于 Looper 线程中的 Handler 对象的 handleMessage 回调方法中。
那么这个消息是怎么被 add 到 Looper 所持有的 MessageQueue 上的呢?答案就在 handler.sendMessage(message) 调用过程中。该过程经过层层调用,最终来到了 MessageQueue 的 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 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"); 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; }
这里保证了队列的结构,即按时间排列和先进先出原则,最后依据 needWake 的值决定是否唤醒队列。
以上就是对于问题的回答。
回答完问题,下面我们来分析下 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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 Message next() { final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } //步骤1 nativePollOnce(ptr, nextPollTimeoutMillis); // might block synchronized (this) { final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; //步骤2 if (msg != null && msg.target == null) { do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } //步骤3 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; } //步骤4 if (mQuitting) { dispose(); return null; } //步骤5 if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { // No idle handlers to run. Loop and wait some more. 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; boolean keep = false; try { keep = idler.queueIdle(); } catch (Throwable t) { Log.wtf(TAG, "IdleHandler threw exception", t); } if (!keep) { synchronized (this) { mIdleHandlers.remove(idler); } } } pendingIdleHandlerCount = 0; nextPollTimeoutMillis = 0; } }
步骤 1 nativePollOnce(ptr, nextPollTimeoutMillis); 是一个 Native 方法,当代码运行于此时,则阻塞,直到有逻辑调用 nativeWake(ptr)方法唤醒,类似于 java 中的 IO 机制。
步骤 2 msg.target==null时,表示该消息是一个屏障消息,于是之后找到队列中的第一个异步消息。
步骤 3 消息不为空并且 when 不超过当前时间点,则返回该消息,否则,则继续进行到步骤 4。
步骤4 mQuitting 变量是标识是否退出 loop 循环的,调用 quit(boolean safe) 方法会设置其值为 true 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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); } }
方法最后调用nativeWake通知next方法往下执行。
步骤5 判断是否有闲时消息,有则处理之。所以,闲时消息在CPU空闲的时候会调用,用于页面启动时加载滞后数据,加快页面启动速度。用法如下:
1 2 3 4 5 val messageQueue = Looper.myQueue() messageQueue.addIdleHandler(MessageQueue.IdleHandler { Log.d("hhhh", "thread=${Thread.currentThread().name},idle") false })
返回 false 表示该闲时消息在每次到达空闲时都会执行一次,true 表示执行一次后抛弃就该消息。
问题2: Message.obtain()方法是用来干什么的? 源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 //android.os.Message public static final Object sPoolSync = new Object(); private static Message sPool; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; 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(); }
sPool 就是缓冲池的头节点,当 sPool 不为 null 时,复用该 Message 即可。当 sPool 为 null 时,则新建 Message 。这样以达到节省内存空间的目的。
缓冲池里的 Message 一定是可复用的,也就是使用完的 Message 。那么我们在哪里去将使用完的 Message 放入缓冲池呢?答案就是 recycleUnchecked 方法。
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 //android.os.Message void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = UID_NONE; workSourceUid = UID_NONE; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }
当 sPoolSize 小于 MAX_POOL_SIZE 时,将已使用完的 Message 作为缓冲池的头指针,同时缓冲池其它的 Message 往后移。
问题3:Handler消息创建和处理在同一个线程时是同步还是异步的? 请看下面的代码。
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 class Main2Activity : AppCompatActivity() { lateinit var handler: Handler override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) handler = object : Handler() { override fun handleMessage(msg: Message) { super.handleMessage(msg) Log.d("TAG", "thread=${Thread.currentThread().name},${msg.what}") } } btnSend.setOnClickListener { val message1 = Message.obtain(handler, 101) handler.sendMessage(message1) Log.d("TAG", "thread=${Thread.currentThread().name},start 1") val message2 = Message.obtain(handler, 102) handler.sendMessage(message2) Log.d("TAG", "thread=${Thread.currentThread().name},start 2") } } } //这段代码的运行结果: thread=main,start 1 thread=main,start 2 thread=main,101 thread=main,102
运行结果可以发现,同一线程消息的发送和处理也是异步的,并不是发送一个消息后立马就处理,原因笔者认为阻塞被唤醒的时机不确定,所以并不是立即就执行后面的代码调用handleMessage回调。
问题4:Handler机制Looper死循环为什么不会导致应用卡死? Android系统的的所有事件处理都是通过Looper机制实现的,当有事件时会通过nativeWake唤醒正在等待的Looper线程,无事件处理时通过nativePollOnce使Looper线程进入休眠状态,也正是这样的机制保证了CPU资源的合理利用。
问题5:Android 执行动画或者绘制 View 时,也是 Handler 吗? 是,具体可参考源码。(源码部分,笔者还不是彻底搞懂,后期搞懂再补充。)