// CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Make sure TrustedCertificateStore looks in the right place for CA certificates finalFileconfigDir= Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir);
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line. // It will be in the format "seq=114" longstartSeq=0; if (args != null) { for (inti= args.length - 1; i >= 0; --i) { if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) { startSeq = Long.parseLong( args[i].substring(PROC_START_SEQ_IDENT.length())); } } } ActivityThreadthread=newActivityThread(); thread.attach(false, startSeq);
if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); }
if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); }
// End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop();
publicvoidhandleResumeActivity(ActivityClientRecord r, boolean finalStateRequest, boolean isForward, String reason) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration // skip below steps for double-resume and r.mFinish = true case. if (!performResumeActivity(r, finalStateRequest, reason)) { return; } if (mActivitiesToBeDestroyed.containsKey(r.token)) { // Although the activity is resumed, it is going to be destroyed. So the following // UI operations are unnecessary and also prevents exception because its token may // be gone that window manager cannot recognize it. All necessary cleanup actions // performed below will be done while handling destruction. return; }
finalActivitya= r.activity;
if (localLOGV) { Slog.v(TAG, "Resume " + r + " started activity: " + a.mStartedActivity + ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished); }
// If the window hasn't yet been added to the window manager, // and this guy didn't finish itself or start another activity, // then go ahead and add the window. booleanwillBeVisible= !a.mStartedActivity; if (!willBeVisible) { willBeVisible = ActivityClient.getInstance().willActivityBeVisible( a.getActivityToken()); } if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); Viewdecor= r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManagerwm= a.getWindowManager(); WindowManager.LayoutParamsl= 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. ViewRootImplimpl= decor.getViewRootImpl(); if (impl != null) { impl.notifyChildRebuilt(); } } if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true; wm.addView(decor, l); } else { // The activity will get a callback for this {@link LayoutParams} change // earlier. However, at that time the decor will not be set (this is set // in this method), so no action will be taken. This call ensures the // callback occurs with the decor set. a.onWindowAttributesChanged(l); } }
// If the window has already been added, but during resume // we started another activity, then don't yet make the // window visible. } elseif (!willBeVisible) { if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set"); r.hideForNow = true; }
// Get rid of anything left hanging around. cleanUpPendingRemoveWindows(r, false/* force */);
// The window is now visible if it has been added, we are not // simply finishing, and we are not starting another activity. if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) { if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward=" + isForward); ViewRootImplimpl= r.window.getDecorView().getViewRootImpl(); WindowManager.LayoutParamsl= impl != null ? impl.mWindowAttributes : r.window.getAttributes(); if ((l.softInputMode & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) != forwardBit) { l.softInputMode = (l.softInputMode & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)) | forwardBit; if (r.activity.mVisibleFromClient) { ViewManagerwm= a.getWindowManager(); Viewdecor= r.window.getDecorView(); wm.updateViewLayout(decor, l); } }
r.activity.mVisibleFromServer = true; mNumVisibleActivities++; if (r.activity.mVisibleFromClient) { r.activity.makeVisible(); } }
r.nextIdle = mNewActivities; mNewActivities = r; if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r); Looper.myQueue().addIdleHandler(newIdler()); }
publicvoidaddView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow, int userId) { if (view == null) { thrownewIllegalArgumentException("view must not be null"); } if (display == null) { thrownewIllegalArgumentException("display must not be null"); } if (!(params instanceof WindowManager.LayoutParams)) { thrownewIllegalArgumentException("Params must be WindowManager.LayoutParams"); }
final WindowManager.LayoutParamswparams= (WindowManager.LayoutParams) params; if (parentWindow != null) { parentWindow.adjustLayoutParamsForSubWindow(wparams); } else { // If there's no parent, then hardware acceleration for this view is // set from the application's hardware acceleration setting. finalContextcontext= view.getContext(); if (context != null && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) { wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; } }
ViewRootImpl root; ViewpanelParentView=null;
synchronized (mLock) { // Start watching for system property changes. if (mSystemPropertyUpdater == null) { mSystemPropertyUpdater = newRunnable() { @Overridepublicvoidrun() { synchronized (mLock) { for (inti= mRoots.size() - 1; i >= 0; --i) { mRoots.get(i).loadSystemProperties(); } } } }; SystemProperties.addChangeCallback(mSystemPropertyUpdater); }
intindex= findViewLocked(view, false); if (index >= 0) { if (mDyingViews.contains(view)) { // Don't wait for MSG_DIE to make it's way through root's queue. mRoots.get(index).doDie(); } else { thrownewIllegalStateException("View " + view + " has already been added to the window manager."); } // The previous removeView() had not completed executing. Now it has. }
// If this is a panel window, then find the window it is being // attached to for future reference. if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { finalintcount= mViews.size(); for (inti=0; i < count; i++) { if (mRoots.get(i).mWindow.asBinder() == wparams.token) { panelParentView = mViews.get(i); } } }
IWindowSessionwindowlessSession=null; // If there is a parent set, but we can't find it, it may be coming // from a SurfaceControlViewHost hierarchy. if (wparams.token != null && panelParentView == null) { for (inti=0; i < mWindowlessRoots.size(); i++) { ViewRootImplmaybeParent= mWindowlessRoots.get(i); if (maybeParent.getWindowToken() == wparams.token) { windowlessSession = maybeParent.getWindowSession(); break; } } }
// do this last because it fires off messages to start doing things try { root.setView(view, wparams, panelParentView, userId); } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. if (index >= 0) { removeViewLocked(index, true); } throw e; } } }
// 确定是否计算 insets. // 如果没有剩余的 inset 侦听器,那么我们可能仍然需要计算 inset,以防旧的 inset 非空并且必须重置。 // insets in case the old insets were non-empty and must be reset. finalbooleancomputesInternalInsets= mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners() || mAttachInfo.mHasNonEmptyGivenInternalInsets;
privatestaticintgetRootMeasureSpec(int windowSize, int measurement, int privateFlags) { int measureSpec; finalintrootDimension= (privateFlags & PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT) != 0 ? MATCH_PARENT : measurement; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: // Window can resize. Set max size for root view. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: // Window wants to be an exact size. Force root view to be that size. measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec; }
protectedvoidmeasureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { finalMarginLayoutParamslp= (MarginLayoutParams) child.getLayoutParams();
publicstaticintgetChildMeasureSpec(int spec, int padding, int childDimension) { intspecMode= MeasureSpec.getMode(spec); intspecSize= MeasureSpec.getSize(spec); intsize= Math.max(0, specSize - padding); intresultSize=0; intresultMode=0; switch (specMode) { // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us case MeasureSpec.AT_MOST: if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let them have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break; } //noinspection ResourceType return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
if (invalidateRoot) { mAttachInfo.mThreadedRenderer.invalidateRoot(); }
dirty.setEmpty();
// Stage the content drawn size now. It will be transferred to the renderer // shortly before the draw commands get send to the renderer. finalbooleanupdated= updateContentDrawBounds();
if (mReportNextDraw) { // report next draw overrides setStopped() // This value is re-sync'd to the value of mStopped // in the handling of mReportNextDraw post-draw. mAttachInfo.mThreadedRenderer.setStopped(false); }
if (updated) { requestDrawWindow(); }
useAsyncReport = true;
if (forceDraw) { mAttachInfo.mThreadedRenderer.forceDrawNextFrame(); } mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this); // 绘制流程 } else { // If we get here with a disabled & requested hardware renderer, something went // wrong (an invalidate posted right before we destroyed the hardware surface // for instance) so we should just bail out. Locking the surface with software // rendering at this point would lock it forever and prevent hardware renderer // from doing its job when it comes back. // Before we request a new frame we must however attempt to reinitiliaze the // hardware renderer if it's in requested state. This would happen after an // eglTerminate() for instance. if (mAttachInfo.mThreadedRenderer != null && !mAttachInfo.mThreadedRenderer.isEnabled() && mAttachInfo.mThreadedRenderer.isRequested() && mSurface.isValid()) {
// register animating rendernodes which started animating prior to renderer // creation, which is typical for animators started prior to first draw if (attachInfo.mPendingAnimatingRenderNodes != null) { finalintcount= attachInfo.mPendingAnimatingRenderNodes.size(); for (inti=0; i < count; i++) { registerAnimatingRenderNode( attachInfo.mPendingAnimatingRenderNodes.get(i)); } attachInfo.mPendingAnimatingRenderNodes.clear(); // We don't need this anymore as subsequent calls to // ViewRootImpl#attachRenderNodeAnimator will go directly to us. attachInfo.mPendingAnimatingRenderNodes = null; }
intsyncResult= syncAndDrawFrame(frameInfo); if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) { Log.w("OpenGLRenderer", "Surface lost, forcing relayout"); // We lost our surface. For a relayout next frame which should give us a new // surface from WindowManager, which hopefully will work. attachInfo.mViewRootImpl.mForceNextWindowRelayout = true; attachInfo.mViewRootImpl.requestLayout(); } if ((syncResult & SYNC_REDRAW_REQUESTED) != 0) { attachInfo.mViewRootImpl.invalidate(); } }
// Consume and set the frame callback after we dispatch draw to the view above, but before // onPostDraw below which may reset the callback for the next frame. This ensures that // updates to the frame callback during scroll handling will also apply in this frame. if (mNextRtFrameCallbacks != null) { final ArrayList<FrameDrawingCallback> frameCallbacks = mNextRtFrameCallbacks; mNextRtFrameCallbacks = null; setFrameCallback(newFrameDrawingCallback() { @Override publicvoidonFrameDraw(long frame) { }
@Override public FrameCommitCallback onFrameDraw(int syncResult, long frame) { ArrayList<FrameCommitCallback> frameCommitCallbacks = newArrayList<>(); for (inti=0; i < frameCallbacks.size(); ++i) { FrameCommitCallbackframeCommitCallback= frameCallbacks.get(i) .onFrameDraw(syncResult, frame); if (frameCommitCallback != null) { frameCommitCallbacks.add(frameCommitCallback); } }
if (frameCommitCallbacks.isEmpty()) { returnnull; }
return didProduceBuffer -> { for (inti=0; i < frameCommitCallbacks.size(); ++i) { frameCommitCallbacks.get(i).onFrameCommit(didProduceBuffer); } }; } }); }
public RenderNode updateDisplayListIfDirty() { finalRenderNoderenderNode= mRenderNode; if (!canHaveDisplayList()) { // can't populate RenderNode, don't try return renderNode; }
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || !renderNode.hasDisplayList() || (mRecreateDisplayList)) { // Don't need to recreate the display list, just need to tell our // children to restore/recreate theirs if (renderNode.hasDisplayList() && !mRecreateDisplayList) { mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchGetDisplayList();
return renderNode; // no work needed }
// If we got here, we're recreating it. Mark it as such to ensure that // we copy in child display lists into ours in drawChild() mRecreateDisplayList = true;
// Hacky hack: Reset any stretch effects as those are applied during the draw pass // instead of being "stateful" like other RenderNode properties renderNode.clearStretch();
finalintchildIndex= getAndVerifyPreorderedIndex(childrenCount, i, customOrder); finalViewchild= getAndVerifyPreorderedView(preorderedList, children, childIndex); if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { more |= drawChild(canvas, child, drawingTime); // 调用 drawChild() 绘制子 view } } while (transientIndex >= 0) { // there may be additional transient views after the normal views finalViewtransientChild= mTransientViews.get(transientIndex); if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE || transientChild.getAnimation() != null) { more |= drawChild(canvas, transientChild, drawingTime); } transientIndex++; if (transientIndex >= transientCount) { break; } } if (preorderedList != null) preorderedList.clear();
// Draw any disappearing views that have animations if (mDisappearingChildren != null) { final ArrayList<View> disappearingChildren = mDisappearingChildren; finalintdisappearingCount= disappearingChildren.size() - 1; // Go backwards -- we may delete as animations finish for (inti= disappearingCount; i >= 0; i--) { finalViewchild= disappearingChildren.get(i); more |= drawChild(canvas, child, drawingTime); } } canvas.disableZ();
if (isShowingLayoutBounds()) { onDebugDraw(canvas); }
if (clipToPadding) { canvas.restoreToCount(clipSaveCount); }
// mGroupFlags might have been updated by drawChild() flags = mGroupFlags;
if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) { invalidate(true); }
if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 && mLayoutAnimationController.isDone() && !more) { // We want to erase the drawing cache and notify the listener after the // next frame is drawn because one extra invalidate() is caused by // drawChild() after the animation is over mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER; finalRunnableend=newRunnable() { @Override publicvoidrun() { notifyAnimationListener(); } }; post(end); } }
TransformationtransformToApply=null; booleanconcatMatrix=false; finalbooleanscalingRequired= mAttachInfo != null && mAttachInfo.mScalingRequired; finalAnimationa= getAnimation(); if (a != null) { more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired); concatMatrix = a.willChangeTransformationMatrix(); if (concatMatrix) { mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM; } transformToApply = parent.getChildTransformation(); } else { if ((mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_TRANSFORM) != 0) { // No longer animating: clear out old animation matrix mRenderNode.setAnimationMatrix(null); mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM; } if (!drawingWithRenderNode && (parentFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) { finalTransformationt= parent.getChildTransformation(); finalbooleanhasTransform= parent.getChildStaticTransformation(this, t); if (hasTransform) { finalinttransformType= t.getTransformationType(); transformToApply = transformType != Transformation.TYPE_IDENTITY ? t : null; concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0; } } }
concatMatrix |= !childHasIdentityMatrix;
// Sets the flag as early as possible to allow draw() implementations // to call invalidate() successfully when doing animations mPrivateFlags |= PFLAG_DRAWN;
if (hardwareAcceleratedCanvas) { // Clear INVALIDATED flag to allow invalidation to occur during rendering, but // retain the flag's value temporarily in the mRecreateDisplayList flag mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0; mPrivateFlags &= ~PFLAG_INVALIDATED; }
RenderNoderenderNode=null; Bitmapcache=null; intlayerType= getLayerType(); // TODO: signify cache state with just 'cache' local if (layerType == LAYER_TYPE_SOFTWARE || !drawingWithRenderNode) { if (layerType != LAYER_TYPE_NONE) { // If not drawing with RenderNode, treat HW layers as SW layerType = LAYER_TYPE_SOFTWARE; buildDrawingCache(true); } cache = getDrawingCache(true); }
if (drawingWithRenderNode) { // Delay getting the display list until animation-driven alpha values are // set up and possibly passed on to the view renderNode = updateDisplayListIfDirty(); if (!renderNode.hasDisplayList()) { // Uncommon, but possible. If a view is removed from the hierarchy during the call // to getDisplayList(), the display list will be marked invalid and we should not // try to use it again. renderNode = null; drawingWithRenderNode = false; } }
intsx=0; intsy=0; if (!drawingWithRenderNode) { computeScroll(); sx = mScrollX; sy = mScrollY; }
if (offsetForScroll) { transX = -sx; transY = -sy; }
if (transformToApply != null) { if (concatMatrix) { if (drawingWithRenderNode) { renderNode.setAnimationMatrix(transformToApply.getMatrix()); } else { // Undo the scroll translation, apply the transformation matrix, // then redo the scroll translate to get the correct result. canvas.translate(-transX, -transY); canvas.concat(transformToApply.getMatrix()); canvas.translate(transX, transY); } parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION; }
if (!drawingWithDrawingCache) { if (drawingWithRenderNode) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; ((RecordingCanvas) canvas).drawRenderNode(renderNode); } else { // Fast path for layouts with no backgrounds if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchDraw(canvas); } else { draw(canvas); } } } elseif (cache != null) { mPrivateFlags &= ~PFLAG_DIRTY_MASK; if (layerType == LAYER_TYPE_NONE || mLayerPaint == null) { // no layer paint, use temporary paint to draw bitmap PaintcachePaint= parent.mCachePaint; if (cachePaint == null) { cachePaint = newPaint(); cachePaint.setDither(false); parent.mCachePaint = cachePaint; } cachePaint.setAlpha((int) (alpha * 255)); canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint); } else { // use layer paint to draw the bitmap, merging the two alphas, but also restore intlayerPaintAlpha= mLayerPaint.getAlpha(); if (alpha < 1) { mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha)); } canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint); if (alpha < 1) { mLayerPaint.setAlpha(layerPaintAlpha); } } }
if (restoreTo >= 0) { canvas.restoreToCount(restoreTo); }
if (a != null && !more) { if (!hardwareAcceleratedCanvas && !a.getFillAfter()) { onSetAlpha(255); } parent.finishAnimatingView(this, a); }
if (more && hardwareAcceleratedCanvas) { if (a.hasAlpha() && (mPrivateFlags & PFLAG_ALPHA_SET) == PFLAG_ALPHA_SET) { // alpha animations should cause the child to recreate its display list invalidate(true); } }
// class FrameDisplayEventReceiver publicvoidonVsync(long timestampNanos, long physicalDisplayId, int frame, VsyncEventData vsyncEventData) { try { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#onVsync " + vsyncEventData.preferredFrameTimeline().vsyncId); } // 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. longnow= 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; }
voiddoFrame(long frameTimeNanos, int frame, DisplayEventReceiver.VsyncEventData vsyncEventData) { finallong startNanos; finallongframeIntervalNanos= vsyncEventData.frameInterval; try { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame " + vsyncEventData.preferredFrameTimeline().vsyncId); } FrameDataframeData=newFrameData(frameTimeNanos, vsyncEventData); synchronized (mLock) { if (!mFrameScheduled) { traceMessage("Frame not scheduled"); return; // no work to do }
longintendedFrameTimeNanos= frameTimeNanos; startNanos = System.nanoTime(); finallongjitterNanos= startNanos - frameTimeNanos; if (jitterNanos >= frameIntervalNanos) { longlastFrameOffset=0; if (frameIntervalNanos == 0) { Log.i(TAG, "Vsync data empty due to timeout"); } else { lastFrameOffset = jitterNanos % frameIntervalNanos; finallongskippedFrames= jitterNanos / frameIntervalNanos; if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) { Log.i(TAG, "Skipped " + skippedFrames + " frames! " + "The application may be doing too much work on its main " + "thread."); } if (DEBUG_JANK) { Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms " + "which is more than the frame interval of " + (frameIntervalNanos * 0.000001f) + " ms! " + "Skipping " + skippedFrames + " frames and setting frame " + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past."); } } frameTimeNanos = startNanos - lastFrameOffset; frameData.updateFrameData(frameTimeNanos); }
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."); } traceMessage("Frame time goes backward"); scheduleVsyncLocked(); return; }
if (mFPSDivisor > 1) { longtimeSinceVsync= frameTimeNanos - mLastFrameTimeNanos; if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) { traceMessage("Frame skipped due to FPSDivisor"); scheduleVsyncLocked(); return; } }
voiddoCallbacks(int callbackType, FrameData frameData, long frameIntervalNanos) { CallbackRecord callbacks; longframeTimeNanos= frameData.mFrameTimeNanos; synchronized (mLock) { // We use "now" to determine when callbacks become due because it's possible // for earlier processing phases in a frame to post callbacks that should run // in a following phase, such as an input event that causes an animation to start. finallongnow= System.nanoTime(); callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS); if (callbacks == null) { return; } mCallbacksRunning = true;
// Update the frame time if necessary when committing the frame. // We only update the frame time if we are more than 2 frames late reaching // the commit phase. This ensures that the frame time which is observed by the // callbacks will always increase from one frame to the next and never repeat. // We never want the next frame's starting frame time to end up being less than // or equal to the previous frame's commit frame time. Keep in mind that the // next frame has most likely already been scheduled by now so we play it // safe by ensuring the commit time is always at least one frame behind. if (callbackType == Choreographer.CALLBACK_COMMIT) { finallongjitterNanos= now - frameTimeNanos; Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos); if (jitterNanos >= 2 * frameIntervalNanos) { finallonglastFrameOffset= jitterNanos % frameIntervalNanos + frameIntervalNanos; if (DEBUG_JANK) { Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f) + " ms which is more than twice the frame interval of " + (frameIntervalNanos * 0.000001f) + " ms! " + "Setting frame time to " + (lastFrameOffset * 0.000001f) + " ms in the past."); mDebugPrintNextFrameTimeDelta = true; } frameTimeNanos = now - lastFrameOffset; mLastFrameTimeNanos = frameTimeNanos; frameData.updateFrameData(frameTimeNanos); } } } try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]); for (CallbackRecordc= callbacks; c != null; c = c.next) { if (DEBUG_FRAMES) { Log.d(TAG, "RunCallback: type=" + callbackType + ", action=" + c.action + ", token=" + c.token + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime)); } c.run(frameData); // 执行 callback } } finally { synchronized (mLock) { mCallbacksRunning = false; do { finalCallbackRecordnext= callbacks.next; recycleCallbackLocked(callbacks); callbacks = next; } while (callbacks != null); } Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
FrameHandler
1 2 3 4 5 6 7 8 9 10 11 12 13
publicvoidhandleMessage(Message msg) { switch (msg.what) { case MSG_DO_FRAME: doFrame(System.nanoTime(), 0, newDisplayEventReceiver.VsyncEventData()); break; case MSG_DO_SCHEDULE_VSYNC: doScheduleVsync(); break; case MSG_DO_SCHEDULE_CALLBACK: doScheduleCallback(msg.arg1); // 对应 postCallbackDelayedInternal() 中的延迟执行 break; } }
// Check whether the child that requests the invalidate is fully opaque // Views being animated or transformed are not considered opaque because we may // be invalidating their old position and need the parent to paint behind them. MatrixchildMatrix= child.getMatrix(); // Mark the child as dirty, using the appropriate flag // Make sure we do not set both flags at the same time
// If the parent is dirty opaque or not dirty, mark it dirty with the opaque // flag coming from the child that initiated the invalidate if (view != null) { if ((view.mPrivateFlags & PFLAG_DIRTY_MASK) != PFLAG_DIRTY) { view.mPrivateFlags = (view.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY; } }
parent = parent.invalidateChildInParent(location, dirty); if (view != null) { // Account for transform on current parent Matrixm= view.getMatrix(); if (!m.isIdentity()) { RectFboundingRect= attachInfo.mTmpTransformRect; boundingRect.set(dirty); m.mapRect(boundingRect); dirty.set((int) Math.floor(boundingRect.left), (int) Math.floor(boundingRect.top), (int) Math.ceil(boundingRect.right), (int) Math.ceil(boundingRect.bottom)); } } } while (parent != null); } }