1.Handler 的诞生 由于 Android 采用的是单线程模式,开发者无法在子线程中更新 UI,因此在 Android 开发中,经常会在子线程中进行一些操作,当操作完成之后,将结果发送到主线程进行显示。探索其背后的模式:子线程、Handler 和主线程三者组成了生产者和消费者模式。子线程负责生产数据,主线程负责消费数据,而 Handler 负责将数据从子线程抛到主线程中 。详细见下图
Handler 生产者消费者模型.png
2.Handler 相关的类
Hanlder:发送和接收消息
Looper:用于轮询消息队列,一个线程只能有一个 Looper
Message: 消息实体
MessageQueue: 消息队列用于存储消息和管理消息。
2.1 Looper 的创建
创建 Looper 的方法是调用 Looper.prepare () 方法或者 Looper.prepareMainLooper ()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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)); } @Deprecated public static void prepareMainLooper () { prepare(false ); synchronized (Looper.class) { if (sMainLooper != null ) { throw new IllegalStateException("The main Looper has already been prepared." ); } sMainLooper = myLooper(); } }
2.2 创建 MessageQueue 以及与 Looper 和当前线程绑定 1 2 3 4 5 6 7 8 9 10 private Looper (boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit(); }
2.3 Looper.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 public static void loop () { final Looper me = myLooper(); if (me == null ) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread." ); } ...... me.mInLoop = true ; final MessageQueue queue = me.mQueue; ...... for (;;) { Message msg = queue.next(); if (msg == null ) { return ; } ...... try { msg.target.dispatchMessage(msg); ...... msg.recycleUnchecked(); } }
2.4 Handler 的创建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Handler handler = new Handler(){ @Override public void handleMessage (Message msg) { super .handleMessage(msg); } }; public Handler (@NonNull Looper looper) { this (looper, null , false ); } public Handler (@NonNull Looper looper, @Nullable Callback callback) { this (looper, callback, false ); }
2.5 Message 的创建 可以直接 new Message () 但是不建议这么使用,因为如果有大量的 new Message () 然后用完了就被回收,这样会导致内存抖动。推荐的方式是使用 obtain () 系列方法。Message 使用了享元模式,内部维护了一个对象池 (最大 50 个),管理者对象的创建和销毁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static Message obtain () { synchronized (sPoolSync) { if (sPool != null ) { Message m = sPool; sPool = m.next; m.next = null ; m.flags = 0 ; sPoolSize--; return m; } } return new Message(); } public static Message obtain (Handler h) public static Message obtain (Handler h, Runnable callback) public static Message obtain (Handler h, int what)
2.6 Message 和 Handhler 绑定
方式 1:通过上面创建 Message 时绑定
方式 2:在发送消息的到时候绑定 (代码如下)
1 2 3 4 5 6 private boolean enqueueMessage (@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { msg.target = this ; ...... return queue.enqueueMessage(msg, uptimeMillis); }
2.7 Handler 发送消息 如下图调用关系,Handler 发送消息的重载方法很多,但是主要只有 2 种。 sendMessage (Message) sendMessage 方法通过一系列重载方法的调用,sendMessage 调用 sendMessageDelayed,继续调用 sendMessageAtTime,继续调用 sendMessageAtTime,继续调用 enqueueMessage,继续调用 MessageQueue 的 enqueueMessage 方法,将消息保存在消息队列中。
发送消息.png
2.8 Handler 消费消息 当 Looper 取出,交给 Handler 的 dispatchMessage 进行处理
我们可以看到在 dispatchMessage 方法中,message 中 callback 是一个 Runnable 对象,如果 callback 不为空,则直接调用 callback 的 run 方法,否则判断 mCallback 是否为空,mCallback 在 Handler 构造方法中初始化,在主线程通直接通过无参的构造方法 new 出来的为 null, 所以会直接执行后面的 handleMessage () 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public void dispatchMessage (@NonNull Message msg) { if (msg.callback != null ) { handleCallback(msg); } else { if (mCallback != null ) { if (mCallback.handleMessage(msg)) { return ; } } handleMessage(msg); } }
3. 难点问题 3.1 消息加入和取出如何保证线程安全 MessageQueue 在消息入队的时候和取消息的时候都会对队列加锁,保证要么入队消息,要么出队消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 boolean enqueueMessage (Message msg, long when) { if (msg.target == null ) { throw new IllegalArgumentException("Message must have a target." ); } synchronized (this ) { ...... } Message next () { ..... for (;;) { ...... synchronized (this ) { ...... }
3.2 消息机制之同步屏障 通过上面分析,通常情况下 Handler 抛的消息会按照时间排序,然后 Looper 从头部开始一个一个取消息执行。但是有个需求是在一个小的时间范围内,handler 抛的消息需要优先执行,那我们应该如何处理呢?例如 UI 需要立马重绘,而且需要优先处理这个消息,那就要用到消息同步屏障。
MessageQueue.postSyncBarrier () // 开启同步屏障
MessageQueue.removeSyncBarrier () // 移除同步屏障
Message.setAsynchronous (true) // 设置为异步消息
3.2.1 开启同步屏障 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 public int postSyncBarrier () { return postSyncBarrier(SystemClock.uptimeMillis()); } private int postSyncBarrier (long when) { 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) { prev = p; p = p.next; } } if (prev != null ) { msg.next = p; prev.next = msg; } else { msg.next = p; mMessages = msg; } return token; } }
此处开启同步屏障之后,从消息池获取的 Message 中 target==null。
3.2.2 处理异步消息 从下面可以看出,当消息队列开启同步屏障的时候(即标识为 msg.target == null
),消息机制在处理消息的时候,优先处理异步消息。这样,同步屏障就起到了一种过滤和优先级的作用。
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 Message next () { ..... for (;;) { if (nextPollTimeoutMillis != 0 ) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this ) { final long now = SystemClock.uptimeMillis(); Message prevMsg = null ; Message msg = mMessages; if (msg != null && msg.target == null ) { do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); } if (msg != null ) { } ..... }
如果开启了同步屏障,在返回的 msg 中会被重新赋值,所以如果开启了同步屏障,MessageQueue 会遍历队列中是否有时间到了的异步消息,如果有就重新给 msg 赋值让他返回出去给 handler 处理。
示意图如下:
同步屏障示意图.png
3.2.3 同步屏障的使用场景 在 View 更新时,draw、requestLayout、invalidate 等很多地方都调用 ViewRootImpl#scheduleTraversals()
,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 void scheduleTraversals () { if (!mTraversalScheduled) { mTraversalScheduled = true ; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null ); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private void postCallbackDelayedInternal (int callbackType, Object action, Object token, long delayMillis) { if (DEBUG_FRAMES) { Log.d(TAG, "PostCallback: type=" + callbackType + ", action=" + action + ", token=" + token + ", delayMillis=" + delayMillis); } synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); if (dueTime <= now) { scheduleFrameLocked(now); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); msg.arg1 = callbackType; msg.setAsynchronous(true ); mHandler.sendMessageAtTime(msg, dueTime); } } }
3.3 同步执行消息 3.3.1 runWithScissors () 分析 我们从前面的分析可以看出生产者 - 消费者模式其实是异步的执行操作。生产者生产好了商品向容器里面放,消费者只管从容器取就行,但是有这么一个场景,就是我生产的东西需要消费了才能继续生产下去,那么就需要考虑同步执行消息了。(可能举例不恰当)但是在 framework 中,WindowManagerService 初始化的时候,就会出现这样的情况,WindowManagerService 的初始化是放在显示子线程 完成的,但是使用多线程来初始化服务虽然加快了初始化速度,但是也会出现一种情况就是某个服务没有初始化结束,导致其他服务运行异常的情况。因此系统提供了 Handler.runWithScissors () 这个方法来实现同步执行消息。如果任务没有执行结束,该线程不能执行其他的任务。下面我们来分析一下这个方法:
1 2 3 4 5 6 public final boolean runWithScissors (@NonNull Runnable r, long timeout) { ...... BlockingRunnable br = new BlockingRunnable(r); return br.postAndWait(this , timeout); }
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 public boolean postAndWait (Handler handler, long timeout) { if (!handler.post(this )) { return false ; } synchronized (this ) { if (timeout > 0 ) { final long expirationTime = SystemClock.uptimeMillis() + timeout; while (!mDone) { long delay = expirationTime - SystemClock.uptimeMillis(); if (delay <= 0 ) { return false ; } try { wait(delay); } catch (InterruptedException ex) { } } } else { while (!mDone) { try { wait(); } catch (InterruptedException ex) { } } } } return true ; }
1 2 3 4 5 6 7 8 9 10 11 public void run () { try { mTask.run(); } finally { synchronized (this ) { mDone = true ; notifyAll(); } } }
3.3.2 runWithScissors () 使用场景 1 2 3 4 5 6 7 8 public static WindowManagerService main (final Context context, final InputManagerService im,final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,Supplier<Surface> surfaceFactory, Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) { DisplayThread.getHandler().runWithScissors(() -> sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,atm, transactionFactory, surfaceFactory, surfaceControlFactory), 0 ); return sInstance; }
参考文章:
同步屏障