当 Activity 启动时候,会调用到 ActivityThread 的 handleResumeActivity 方法。在了解此篇文章的时候,先了解 Activity 、 Window 、 View 之间的关系,从这里可以了解到 PhoneWindow、DecorView 和 ViewGroup/View 的关系。此篇文章将从 ActivityThread 中的 handleResumeActivity 开始解析。
在 ActivityThread 的 handleResumeActivity 中:
@Override public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) { ... final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); if (r == null) { // We didn't actually resume the activity, so skipping any follow-up actions. return; } final Activity a = r.activity; if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (r.mPreserveWindow) { a.mWindowAdded = true; r.mPreserveWindow = false; // Normally the ViewRoot sets up callbacks with the Activity // in addView->ViewRootImpl#setView. If we are instead reusing // the decor view we have to notify the view root that the // callbacks may have changed. ViewRootImpl impl = decor.getViewRootImpl(); if (impl != null) { impl.notifyChildRebuilt(); } } if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true; wm.addView(decor, l); // 1 } else { a.onWindowAttributesChanged(l); } } } else if (!willBeVisible) { if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set"); r.hideForNow = true; } ... }这里的核心代码 wm.addView(decor, l), vm 是 WindowManagerImpl,decor 是 DecorView。其中 WindowManagerImpl 的继承关系如图所示:
WindowManagerImpl 中 addView 方法:
@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); }调用 WindowManagerGlobal 的 addView 方法,这里的 view 是前面传递进来的 decorView,addView 方法如下:
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ... ViewRootImpl root; View panelParentView = null; synchronized (mLock) { ... root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); // do this last because it fires off messages to start doing things try { root.setView(view, wparams, panelParentView); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. if (index >= 0) { removeViewLocked(index, true); } throw e; } } }此方法中创建了 ViewRootImpl 对象,并且调用 ViewRootImpl 的 setView 方法,并且将 decorView 传递进去,这样 ViewRootImpl 就跟 decorView 联系起来了,ViewRootImpl 的继承关系如下:
setView 方法:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; ... // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system. requestLayout(); ... } } }此方法中的核心是调用了 requestLayout 方法:
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { checkThread(); mLayoutRequested = true; scheduleTraversals(); } }接着调用了 scheduleTraversals 方法:
@UnsupportedAppUsage void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }通过调用 Choreographer 启动一个TraversalRunnable:
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } }接着调用 doTraversal 方法:
void doTraversal() { if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }接着调用 performTraversals 方法:
private void performTraversals() { // cache mView since it is used so much below... final View host = mView; ... if (!mStopped || mReportNextDraw) { ... int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); ... performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); ... } ... if (didLayout) { performLayout(lp, mWidth, mHeight); ... } ... performDraw(); ... }核心就是三个方法:performMeasure、performLayout、performDraw。
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { if (mView == null) { return; } Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); try { mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }这里的 mView 是前面传递过来的 DecorView,DecorView 是 FrameLayout ,本质是一个 ViewGroup。所以一开始执行的是 ViewGroup 的 measure 方法,并且按照以下的流程逐一对子 View 进行测量:
这里以 DecorView 为例子,在 DecorView 和 ViewGroup 中是找不到 measure 方法,measure 方法的实现在父类 View 中:
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { ... if (forceLayout || needsLayout) { ... if (cacheIndex < 0 || sIgnoreMeasureCache) { // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; } else { ... } ... mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; } ... }这里的核心方法是调用了自己本身的 onMeasure 方法,对于 DecorView 来讲,onMeasure 方法的实现在 FrameLayout 中:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int count = getChildCount(); ... count = mMatchParentChildren.size(); if (count > 1) { for (int i = 0; i < count; i++) { final View child = mMatchParentChildren.get(i); ... child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } } }如果子 View 的个数大于0,则又调用了子 View 的 measure 方法,整体逻辑符合上面的流程图。
Android 屏幕每16毫秒会刷新一次,也就是每秒会刷新60次,人眼能感觉到卡顿的帧率是每秒24帧。要求我们的应用都能在 16ms 内绘制完成。如果有一次的界面绘制用了 22ms,那么,用户在 32ms 内看见的都是同一个界面。就会让用户觉得卡顿。而 Android 是如何进行刷新的呢?
在上面的流程中,方法 scheduleTraversals:
@UnsupportedAppUsage void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();//1 mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//2 if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }在上面的分析中,并没有具体分析这里面执行的方法,接下来将具体分析下。
注释1:设置了同步屏障,这里的作用是为了更快的响应 Ui 刷新事件。设置了同步屏障之后,同步屏障为 Handler 消息机制增加了一种简单的优先级机制,异步消息的优先级要高于同步消息。关于同步屏障,可以参考Handler之同步屏障机制(sync barrier)。
在注释 2 中调用了 Choreographer 的 postCallback 方法:
@UnsupportedAppUsage @TestApi public void postCallback(int callbackType, Runnable action, Object token) { postCallbackDelayed(callbackType, action, token, 0); }postCallbackDelayed 方法,这里的参数 delayMillis 传递 0 进来:
@UnsupportedAppUsage @TestApi 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); }postCallbackDelayedInternal 方法:
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); } } }由于传递进来的 delayMillis 为0,则执行 scheduleFrameLocked 方法:
private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled = true; if (USE_VSYNC) { if (DEBUG_FRAMES) { Log.d(TAG, "Scheduling next frame on vsync."); } // If running on the Looper thread, then schedule the vsync immediately, // otherwise post a message to schedule the vsync from the UI thread // as soon as possible. if (isRunningOnLooperThreadLocked()) { // 3 scheduleVsyncLocked(); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg); } } else { final long nextFrameTime = Math.max( mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now); if (DEBUG_FRAMES) { Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms."); } Message msg = mHandler.obtainMessage(MSG_DO_FRAME); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, nextFrameTime); } } }注释3:判断当前线程是 UI线程,执行 scheduleVsyncLocked 方法:
@UnsupportedAppUsage private void scheduleVsyncLocked() { mDisplayEventReceiver.scheduleVsync(); }调用 DisplayEventReceiver 的 scheduleVsync 方法 ,DisplayEventReceiver 是一个抽象类,FrameDisplayEventReceiver 是它的实现类:
/** * Schedules a single vertical sync pulse to be delivered when the next * display frame begins. */ @UnsupportedAppUsage 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 { nativeScheduleVsync(mReceiverPtr); } }这里调用了一个 native层的方法,此方法的作用:通知底层,下一个 VSync 信号来的时候请通知我,当 VSync 信号来的时候,就会收到底层的 JNI 回调,会调用到 dispatchVsync 方法:
// Called from native code. @SuppressWarnings("unused") @UnsupportedAppUsage private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) { onVsync(timestampNanos, physicalDisplayId, frame); }onVsync 方法的实现在 Choreographer 的内部类 FrameDisplayEventReceiver 中:
private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { private boolean mHavePendingVsync; private long mTimestampNanos; private int mFrame; public FrameDisplayEventReceiver(Looper looper, int vsyncSource) { super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS); } // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for // the internal display and DisplayEventReceiver#scheduleVsync only allows requesting VSYNC // for the internal display implicitly. @Override public void onVsync(long timestampNanos, long physicalDisplayId, int frame) { // Post the vsync event to the Handler. // The idea is to prevent incoming vsync events from completely starving // the message queue. If there are no messages in the queue with timestamps // earlier than the frame time, then the vsync event will be processed immediately. // Otherwise, messages that predate the vsync event will be handled first. long now = System.nanoTime(); if (timestampNanos > now) { Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f) + " ms in the future! Check that graphics HAL is generating vsync " + "timestamps using the correct timebase."); 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); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS); } @Override public void run() { mHavePendingVsync = false; doFrame(mTimestampNanos, mFrame); } }当接收到信号时候,就会执行 run 方法中的 doFrame 方法:
@UnsupportedAppUsage void doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { ... long intendedFrameTimeNanos = frameTimeNanos;//设置当前帧的Vsync信号到来时间 startNanos = System.nanoTime();//实际开始执行当前帧的时间 final long jitterNanos = startNanos - frameTimeNanos; if (jitterNanos >= mFrameIntervalNanos) {//时间差大于一个时钟周期,认为跳帧 final long skippedFrames = jitterNanos / mFrameIntervalNanos; if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) { Log.i(TAG, "Skipped " + skippedFrames + " frames! " + "The application may be doing too much work on its main thread."); } final long lastFrameOffset = jitterNanos % mFrameIntervalNanos; frameTimeNanos = startNanos - lastFrameOffset;// 出现掉帧,把时间修正一下,对比的是上一帧时间 } //时间倒退了,可能是由于改了系统时间,此时就重新申请vsync信号(一般不会走这里) if (frameTimeNanos < mLastFrameTimeNanos) {// if (DEBUG_JANK) { Log.d(TAG, "Frame time appears to be going backwards. May be due to a " + "previously skipped frame. Waiting for next vsync."); } scheduleVsyncLocked(); return; } if (mFPSDivisor > 1) { long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos; if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) { scheduleVsyncLocked();//申请下一次vsync信号,流程跟上面分析一样 return; } } mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos); mFrameScheduled = false; mLastFrameTimeNanos = frameTimeNanos; } 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(); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); } finally { AnimationUtils.unlockAnimationClock(); Trace.traceEnd(Trace.TRACE_TAG_VIEW); } ... }计算收到 VSync 信号到 doFrame 被调用的时间差,VSync 信号间隔是16毫秒一次,大于16毫秒就是掉帧。如果超过30帧(默认30),就打印log提示开发者检查主线程是否有耗时操作。
接着执行 doCallbacks 方法:
void doCallbacks(int callbackType, long frameTimeNanos) { CallbackRecord callbacks; synchronized (mLock) { final long now = System.nanoTime(); callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS); if (callbacks == null) { return; } mCallbacksRunning = true; ... } try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]); for (CallbackRecord c = callbacks; c != null; c = c.next) { ... c.run(frameTimeNanos); } } finally { ... } }这里的核心的就是取出队列中任务,也就是 doTraversal 方法。
当 Activity 启动时候,会调用到 ActivityThread 的 handleResumeActivity 方法,接着调用 ViewRootImpl 的 setView 方法。setView 方法中会调用 requestLayout,requestLayout 方法中调用 scheduleTraversals,接着 doTraversal ,最终调用到了 performTraversals。接着分别执行 performMeasure、performLayout、performDraw 方法。performMeasure 方法中首先会执行 measure 方法,接着执行 onMeasure 方法。在 onMeasure 方法中如果当前 View 存在子 View,则遍历执行子 View 的 measure ,最终完成测量的工作。performLayout 和 performDraw 的逻辑也一样。
在 scheduleTraversals 方法中,通过 Choreographer 类向 native 请求 VSync(垂直同步)信号,下一次 VSync(垂直同步)信号来的时候通过 JNI 调用 onVsync 方法通知应用层,并把消息发送到主线程,请求执行 doFrame 渲染下一帧。
