写点什么

Handler 源码分析之二 异步消息的处理,2021 金三银四面试季

用户头像
Android架构
关注
发布于: 19 小时前

//回收 p.recycleUnchecked();


//如果需要唤醒,则唤醒等待 if (needWake && !mQuitting) {nativeWake(mPtr);}}}


然后看 mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);第一个参数是个 int,第二个参数 mTraversalRunnable 是个 Runnable,代码如下:


final TraversalRunnable mTraversalRunnable = new TraversalRunnable();final class TraversalRunnable implements Runnable {@Overridepublic void run() {doTraversal();}}


void doTraversal() {if (mTraversalScheduled) { //不在执行中才执行 mTraversalScheduled = false; //更新标记为执行中//从 mHandler 的 MesssageQueue 中移除同步屏障,还记得刚刚添加同步屏障的代码吗 mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);//开始遍历布局 performTraversals();}}


//这个方法对 view 进行了测量,布局和绘制,我们后面会细讲 private void performTraversals() {......performMeasure();......performLayout();......performDraw();......}


我们来看下 Choreographer 类的部分关键代码:


class Choreographer {public static final int CALLBACK_INPUT = 0;public static final int CALLBACK_ANIMATION = 1;public static final int CALLBACK_INSETS_ANIMATION = 2;public static final int CALLBACK_TRAVERSAL = 3; //这是刚刚的第一个参数 public static final int CALLBACK_COMMIT = 4;private static final int CALLBACK_LAST = CALLBACK_COMMIT;private final CallbackQueue[] mCallbackQueues;private final FrameHandler mHandler;private final FrameDisplayEventReceiver mDisplayEventReceiver;


private Choreographer(Looper looper, int vsyncSource) {mLooper = looper;


mHandler = new FrameHandler(looper);


//这里初始化 mDisplayEventReceivermDisplayEventReceiver = USE_VSYNC? new FrameDisplayEventReceiver(looper, vsyncSource): null;mLastFrameTimeNanos = Long.MIN_VALUE;


mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());


//这里对 mCallbackQueues 进行初始化,直接 new 出来一个长度为 5 的数组,并且初始化 5 个元素,为什么长度是 5?因为上面说了 CALLBACK_LAST=4mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];for (int i = 0; i <= CALLBACK_LAST; i++) {mCallbackQueues[i] = new CallbackQueue();}}


//这就是刚刚调用的方法 public void postCallback(int callbackType, Runnable action, Object token) {postCallbackDelayed(callbackType, action, token, 0); //第四个参数是 0}


public void postCallbackDelayed(int callbackType,Runnable action, Object token, long delayMillis) {if (action == null) {throw new IllegalArgumentException("action must not be null");}if (callbackType < 0 || callbackType > CALLBACK_LAST) {throw new IllegalArgumentException("callbackType is invalid");}


postCallbackDelayedInternal(callbackType, action, token, delayMillis);}


//最终调到这里 private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {synchronized (mLock) {final long now = SystemClock.uptimeMillis(); //获取当前时间 final long dueTime = now + delayMillis; //delayMillis = 0,所以 dueTime = now//这是个数组 callbackType 是 CALLBACK_TRAVERSAL,dueTime=0,action 就是那个 mTraversalRunnable//这里就是将这个 mTraversalRunnable 保存在 CALLBACK_TRAVERSAL 对应的集合里了 mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);


if (dueTime <= now) { //truescheduleFrameLocked(now); //跑这里} else {//否则通过 Handler 发送出去 Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);msg.arg1 = callbackType;msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, dueTime);}}}


//接着看 private void scheduleFrameLocked(long now) {if (!mFrameScheduled) {mFrameScheduled = true;if (USE_VSYNC) {//看这里 if (isRunningOnLooperThreadLocked()) {scheduleVsyncLocked();} else {Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); //msg.what = MSG_DO_SCHEDULE_VSYNCmsg.setAsynchronous(true); //标记为异步消息!! 前面我们加了个同步屏障了的 mHandler.sendMessageAtFrontOfQueue(msg); //发送,这个 mHandler 是 FrameHandler}} else {final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);Message msg = mHandler.obtainMessage(MSG_DO_FRAME);msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, nextFrameTime);}}}}


接着来看 FrameHandler 的代码:


private final class FrameHandler extends Handler {public FrameHandler(Looper looper) {super(looper);}


@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_DO_FRAME:doFrame(System.nanoTime(), 0);break;case MSG_DO_SCHEDULE_VSYNC: //跑到了这里 doScheduleVsync();break;case MSG_DO_SCHEDULE_CALLBACK:doScheduleCallback(msg.arg1);break


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


;}}}


接着看


void doScheduleVsync() {synchronized (mLock) {if (mFrameScheduled) {scheduleVsyncLocked();}}}


private void scheduleVsyncLocked() {mDisplayEventReceiver.scheduleVsync();}


//来看下 scheduleVsync 方法:public void scheduleVsync() {if (mReceiverPtr == 0) {Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "


  • "receiver has already been disposed.");} else {//这里直接调用了 native 的方法,native 调用完成会回到 nativeScheduleVsync(mReceiverPtr);}}


native 完事后会回调到 FrameDisplayEventReceiver.onVsync()里面:


private final class FrameDisplayEventReceiver extends DisplayEventReceiverimplements Runnable {private boolean mHavePendingVsync;private long mTimestampNanos;private int mFrame;


public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS);}


@Overridepublic void onVsync(long timestampNanos, long physicalDisplayId, int frame) {long now = System.nanoTime();if (timestampNanos > now) {timestampNanos = now;}


if (mHavePendingVsync) {Log.w(TAG, "Already have a pending vsync event. There should only be "


  • "one at a time.");} else {mHavePendingVsync = true;}


mTimestampNanos = timestampNanos;mFrame = frame;Message msg = Message.obtain(mHandler, this); //这里的第二个参数是 this,意味着 msg.callback = this,那么就会跑到自己的 run 函数 msg.setAsynchronous(true);mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);}


@Overridepublic void run() {//跑到这里了 mHavePendingVsync = false;doFrame(mTimestampNanos, mFrame);}}


void doFrame(long frameTimeNanos, int frame) {final long startNanos;synchronized (mLock) {if (!mFrameScheduled) {return; // no work to do}...省略计算时间的代码...//下面开始执行事件顺序为: 1 输入事件 2 动画 3 布局 try {Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);


mFrameInfo.markInputHandlingStart();doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);


mFrameInfo.markAnimationsStart();doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);


mFrameInfo.markPerformTraversalsStart();//TAG: CALLBACK_TRAVERSAL,还记得这个 TAG 吗,我们看这里即可 doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);


doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);} finally {AnimationUtils.unlockAnimationClock();Trace.traceEnd(Trace.TRACE_TAG_VIEW);}}


void doCallbacks(int callbackType, long frameTimeNanos) {CallbackRecord callbacks;synchronized (mLock) {//这里根据 callbackType 取出所有事件,这里的 type 是:CALLBACK_TRAVERSALcallbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now / TimeUtils.NANOS_PER_MS);if (callbacks == null) {return;}mCallbacksRunning = true;...省略部分代码...}try {//遍历取出所有事件,执行,还记得我们之前的那个:mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)吗?//第二个参数就是 i 一个 i 饿 runnable,就会被执行,也就是执行到我们的 traversal()里去了,最终就会去测量、布局、绘制 for (CallbackRecord c = callbacks; c != null; c = c.next) {c.run(frameTimeNanos);}} finally {...回收 callback...}}


通过以上分析我们了解到: Android 在布局的时候使用了异步消息,为什么要使用异步消息呢,有什么好处,答案就是:异步消息会被优先处理,比如当前 MessageQueue 中有 100 个消息,这时候有个布局消息来了,正常的话要等待 100 个执行完,如果是异步消息,则可以优先执行,这样 UI 就不会因为消息过多而卡顿,这就是优点,我们再来看看 MessageQueue.next 的代码:


Message next() {...省略部分代码...for (;;) {...省略部分代码...synchronized (this) {final long now = SystemClock.uptimeMillis(); //记录当前时间 Message prevMsg = null;Message msg = mMessages; //取出第一条消息,也就是队头消息


//msg.target == null! 上面分析过,这是一个同步屏障,而同步屏障后面一般会有一个人异步消息,同步屏障的目的就是为了标记后面有个异步消息的 if (msg != null && msg.target == null) { //如果有同步屏障,那么就找异步消息 do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous()); //果然在这里,只要不是异步消息,就一直找,直到找到为止}


//开始处理消息 if (msg != null) {...处理 msg...} else {// 标记为-1 无限等待 nextPollTimeoutMillis = -1;}...省略部分代码...}...省略处理空闲消息的代码...}}


上面省略了部分代码,主要分析了 异步消息是被优先执行的,而且异步消息都跟在同步屏障后面的

2 为什么要把 Looper 放在 ThreadLocal 里面

我们先来看怎么把 Looper 放在 ThreadLocal 里面的? Looper.prepare()


static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Handler源码分析之二 异步消息的处理,2021金三银四面试季