WindowManagerService动画分析
来源:互联网 发布:电子数据调查分析师 编辑:程序博客网 时间:2024/06/17 12:18
我们知道Android动画主要包括Activity切换动画、窗口动画、转屏动画、窗口内部View动画,前三者在WindowManagerService(简称WMS)中完成,本篇文章重点分析Activity切换动画、窗口动画的设置,动画每一帧计算,及如何快速分析动画类型资源。阅读本文最好有一定的WindowManagerService基础,本文所有代码基于AndroidM。
典型的Activity切换动画包括:activityOpenEnterAnimation,activityOpenExitAnimation,taskOpenEnterAnimation, taskCloseExitAnimation, wallpaperOpenEnterAnimation, wallpaperCloseEnterAnimation。典型的窗口动画包括:windowEnterAnimation,windowExitAnimation。
我们知道每一个Activity在WMS中对应有一个APPWindowToken,APPWindowToken内部有一个AppWindowAnimator类型对象,Activity切换动画逻辑便是在AppTransition、AppWindowAnimator中完成。每个Activity一般情况下都有一个窗口,窗口是用WindowState来描述的,每个窗口有一个WindowStateAnimator类型对象,这个对象管理着窗口Surface和窗口动画。有了这些基本信息,下面就来分析Activity切换和窗口动画。
文章会分为四个部分,分别为:Activity切换动画设置,窗口动画设置,动画每一帧计算,通过日志快速分析动画资源来自哪里。
第一部分:Activity切换动画设置
第1步、WMS.prepareAppTransition()
AMS调用WMS.prepareAppTransition()设置Activity切换动画类型。比较常见的就是TRANSIT_ACTIVITY_OPEN、TRANSIT_ACTIVITY_CLOSE、TRANSIT_TASK_OPEN、TRANSIT_TASK_CLOSE、TRANSIT_TASK_TO_FRONT、TRANSIT_TASK_TO_BACK这几种Activity切换动画类型了。prepareAppTransition()函数做了两件事:①将动画类型设置到AppTransition中,后面将用到这个值;②发送一个APP_TRANSITION_TIMEOUT的5s超时消息。
第2步、WMS.setAppVisibility()
AMS调用WMS.setAppVisibility()设置Activity可见性。一般情况下在一次Activity切换过程中会有两个Activity的可见性发生变化,pre Activity变为不可见,next Activity变为可见。这个函数有两条逻辑:①前面已经调用WMS.prepareAppTransition()设置了Activity切换动画类型,那么将可见Activity加入mOpeningApps,不可见Activity加入mClosingApps,同时调用AppWindowToken.sendAppVisibilityToClients()通知应用窗口可见性变化。mOpeningApps、mClosingApps这两个变量就是为Activity切换动画而存在的,将在后面用到;②没有调用过WMS.prepareAppTransition(),那么直接调用WMS.setTokenVisibilityLocked(),这个函数将在第11步中分析。
public void setAppVisibility(IBinder token, boolean visible) { ...... synchronized(mWindowMap) { wtoken = findAppWindowToken(token); ...... // If we are preparing an app transition, then delay changing // the visibility of this token until we execute that transition. if (okToDisplay() && mAppTransition.isTransitionSet()) { ...... wtoken.inPendingTransaction = true; if (visible) { mOpeningApps.add(wtoken); //将可见Activity加到mOpeningApps中; wtoken.startingMoved = false; wtoken.mEnteringAnimation = true; // If the token is currently hidden (should be the // common case), then we need to set up to wait for // its windows to be ready. if (wtoken.hidden) { wtoken.allDrawn = false; wtoken.deferClearAllDrawn = false; wtoken.waitingToShow = true; if (wtoken.clientHidden) { // In the case where we are making an app visible // but holding off for a transition, we still need // to tell the client to make its windows visible so // they get drawn. Otherwise, we will wait on // performing the transition until all windows have // been drawn, they never will be, and we are sad. wtoken.clientHidden = false; wtoken.sendAppVisibilityToClients(); //这个调用就是通知应用窗口可见性变化; } } } else { mClosingApps.add(wtoken); //不可见Activity加到mClosingApps中; wtoken.mEnteringAnimation = false; } ...... return; } ...... setTokenVisibilityLocked(wtoken, null, visible, AppTransition.TRANSIT_UNSET,true, wtoken.voiceInteraction); } }
第3、4步、AppWindowToken.sendAppVisibilityToClients()/dispatchAppVisibility()
这个函数就是通知上层应用窗口可见性发生变化。如果next Activity是冷启动,那么该函数并不能通知next Activity的窗口变为可见,因为此时该函数调用时,next Activity的窗口还没add到WMS中来。
第5、6步、WMS.finishDrawingWindow()
next Activity被Resume起来后,添加窗口、measure、layout、draw等一系列操作完成后便会调用WMS.finishDrawingWindow()来通知WMS,该窗口已经绘制好了,可以开始做动画了。WMS.finishDrawingWindow()会调用WindowStateAnimator.finishDrawingLocked()更新窗口状态mDrawState为COMMIT_DRAW_PENDING。同时还会调用requestTraversalLocked()来触发一次界面刷新函数performLayoutAndPlaceSurfacesLockedInner()调用。
第7步、performLayoutAndPlaceSurfacesLockedInner()
该函数就是鼎鼎大名的界面刷新函数。该函数里面跟Activity切换动画相关的调用拆分到接下来的那些步骤中。
第8步、WindowStateAnimator.commitFinishDrawingLocked()
performLayoutAndPlaceSurfacesLockedInner()函数中,对于窗口堆栈中有Surface的窗口均会调用WindowStateAnimator.commitFinishDrawingLocked(),该函数将窗口状态为COMMIT_DRAW_PENDING或READY_TO_SHOW的窗口,全部更新到READY_TO_SHOW状态。将状态从第五中中的COMMIT_DRAW_PENDING更新到READY_TO_SHOW,WindowState.isDrawnLw()便会返回true,代表窗口已经是绘制完成状态。w.isDrawnLw()返回为true,那么才有updateAllDrawn = true,接着才会触发调用第九步中的WMS.updateAllDrawnLocked()函数,将AppWindowToken.allDrawn置为true。
boolean commitFinishDrawingLocked() { if (DEBUG_STARTING_WINDOW && mWin.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION_STARTING) { Slog.i(TAG, "commitFinishDrawingLocked: " + mWin + " cur mDrawState=" + drawStateToString()); } if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) { return false; } if (DEBUG_SURFACE_TRACE || DEBUG_ANIM) { Slog.i(TAG, "commitFinishDrawingLocked: mDrawState=READY_TO_SHOW " + mSurfaceControl); } mDrawState = READY_TO_SHOW; //将窗口状态为COMMIT_DRAW_PENDING或READY_TO_SHOW更新到READY_TO_SHOW; final AppWindowToken atoken = mWin.mAppToken; if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) { return performShowLocked(); //对于Activity切换这种场景,此时该条件无法满足,故不会调用。 } return false; }
if (w != atoken.startingWindow) { if (!atoken.mAppAnimator.freezingScreen || !w.mAppFreezing) { atoken.numInterestingWindows++; //计算属于该AppWindowToken的窗口总数; if (w.isDrawnLw()) { atoken.numDrawnWindows++; //计算已经绘制完成的窗口总数; if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Slog.v(TAG, "tokenMayBeDrawn: " + atoken + " freezingScreen=" + atoken.mAppAnimator.freezingScreen + " mAppFreezing=" + w.mAppFreezing); updateAllDrawn = true; } }
第9步、WMS.updateAllDrawnLocked()
该函数更新AppWindowToken.allDrawn值。只有属于该AppWindowToken的所有窗口都是绘制完成状态(一般情况下只有一个窗口,有时候会有父窗口、子窗口,这时属于该AppWindowToken的窗口数量就不止一个了),AppWindowToken.allDrawn才会置为true。AppWindowToken.allDrawn为true才会使得第十步中的WMS.handleAppTransitionReadyLocked()完整的执行。
private void updateAllDrawnLocked(DisplayContent displayContent) { // See if any windows have been drawn, so they (and others // associated with them) can now be shown. ArrayList<TaskStack> stacks = displayContent.getStacks(); for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { final ArrayList<Task> tasks = stacks.get(stackNdx).getTasks(); for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { final AppTokenList tokens = tasks.get(taskNdx).mAppTokens; for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { final AppWindowToken wtoken = tokens.get(tokenNdx); if (!wtoken.allDrawn) { int numInteresting = wtoken.numInterestingWindows; if (numInteresting > 0 && wtoken.numDrawnWindows >= numInteresting) { //绘制完成窗口总数>=窗口总数,就代表属于该AppWindowToken的所有窗口均已绘制完成; if (DEBUG_VISIBILITY) Slog.v(TAG, "allDrawn: " + wtoken + " interesting=" + numInteresting + " drawn=" + wtoken.numDrawnWindows); wtoken.allDrawn = true; //将allDrawn置为true; // Force an additional layout pass where WindowStateAnimator# // commitFinishDrawingLocked() will call performShowLocked(). displayContent.layoutNeeded = true; mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, wtoken.token).sendToTarget(); } } } } } }
第10步、WMS.handleAppTransitionReadyLocked()
设置Activity切换动画选择逻辑就是在该函数中完成。该函数只有在调用了第一步后才会被调用,并且只有在wtoken.allDrawn=true时才会完整的执行完。这个函数接近300行,逻辑看起来非常复杂,下面将仔细分析这个函数。
public int handleAppTransitionReadyLocked(WindowList windows) { int changes = 0; int i; int appsCount = mOpeningApps.size(); boolean goodToGo = true; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Checking " + appsCount + " opening apps (frozen=" + mDisplayFrozen + " timeout=" + mAppTransition.isTimeout() + ")..."); if (!mAppTransition.isTimeout()) { for (i = 0; i < appsCount && goodToGo; i++) { AppWindowToken wtoken = mOpeningApps.valueAt(i); if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Check opening app=" + wtoken + ": allDrawn=" + wtoken.allDrawn + " startingDisplayed=" + wtoken.startingDisplayed + " startingMoved=" + wtoken.startingMoved); if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) { //wtoken.allDrawn=true,goodToGo才不会被置为false,下面的逻辑才能继续执行; goodToGo = false; } } if (goodToGo && isWallpaperVisible(mWallpaperTarget)) { //如果壁纸需要可见,但是壁纸此时是不可见状态,那么就需要等待壁纸绘制完成。。。才能继续执行下面的Activity切换动画选择逻辑; boolean wallpaperGoodToGo = true; for (int curTokenIndex = mWallpaperTokens.size() - 1; curTokenIndex >= 0 && wallpaperGoodToGo; curTokenIndex--) { WindowToken token = mWallpaperTokens.get(curTokenIndex); for (int curWallpaperIndex = token.windows.size() - 1; curWallpaperIndex >= 0; curWallpaperIndex--) { WindowState wallpaper = token.windows.get(curWallpaperIndex); if (wallpaper.mWallpaperVisible && !wallpaper.isDrawnLw()) { // We've told this wallpaper to be visible, but it is not drawn yet wallpaperGoodToGo = false; if (mWallpaperDrawState != WALLPAPER_DRAW_TIMEOUT) { // wait for this wallpaper until it is drawn or timeout goodToGo = false; } if (mWallpaperDrawState == WALLPAPER_DRAW_NORMAL) { mWallpaperDrawState = WALLPAPER_DRAW_PENDING; mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT); mH.sendEmptyMessageDelayed(H.WALLPAPER_DRAW_PENDING_TIMEOUT, WALLPAPER_DRAW_PENDING_TIMEOUT_DURATION); } if (DEBUG_APP_TRANSITIONS || DEBUG_WALLPAPER) Slog.v(TAG, "Wallpaper should be visible but has not been drawn yet. " + "mWallpaperDrawState=" + mWallpaperDrawState); break; } } } if (wallpaperGoodToGo) { mWallpaperDrawState = WALLPAPER_DRAW_NORMAL; mH.removeMessages(H.WALLPAPER_DRAW_PENDING_TIMEOUT); } } } if (goodToGo) { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); int transit = mAppTransition.getAppTransition(); //取出第一步中设置的Activity切换动画类型; if (mSkipAppTransitionAnimation) { //如果设置了StartingWindow窗口,那么就忽略第一步中设置的Activity切换动画类型; transit = AppTransition.TRANSIT_UNSET; } mSkipAppTransitionAnimation = false; mNoAnimationNotifyOnTransitionFinished.clear(); mH.removeMessages(H.APP_TRANSITION_TIMEOUT); //移除超时; rebuildAppWindowListLocked(); //窗口堆栈顺序重排,偶现Activity窗口显示在launcher图标之下就可能就是窗口堆栈顺序没有重排导致,Android4.4就有该问题。 // if wallpaper is animating in or out set oldWallpaper to null else to wallpaper WindowState oldWallpaper = mWallpaperTarget != null && mWallpaperTarget.mWinAnimator.isAnimating() && !mWallpaperTarget.mWinAnimator.isDummyAnimation() ? null : mWallpaperTarget; //oldWallpaper指向null或mWallpaperTarget mInnerFields.mWallpaperMayChange = false; // The top-most window will supply the layout params, // and we will determine it below. LayoutParams animLp = null; int bestAnimLayer = -1; boolean fullscreenAnim = false; boolean voiceInteraction = false; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New wallpaper target=" + mWallpaperTarget + ", oldWallpaper=" + oldWallpaper + ", lower target=" + mLowerWallpaperTarget + ", upper target=" + mUpperWallpaperTarget); boolean openingAppHasWallpaper = false; boolean closingAppHasWallpaper = false; final AppWindowToken lowerWallpaperAppToken; final AppWindowToken upperWallpaperAppToken; if (mLowerWallpaperTarget == null) { lowerWallpaperAppToken = upperWallpaperAppToken = null; } else { lowerWallpaperAppToken = mLowerWallpaperTarget.mAppToken; upperWallpaperAppToken = mUpperWallpaperTarget.mAppToken; } // Do a first pass through the tokens for two // things: // (1) Determine if both the closing and opening // app token sets are wallpaper targets, in which // case special animations are needed // (since the wallpaper needs to stay static // behind them). // (2) Find the layout params of the top-most // application window in the tokens, which is // what will control the animation theme. final int closingAppsCount = mClosingApps.size(); appsCount = closingAppsCount + mOpeningApps.size(); for (i = 0; i < appsCount; i++) { //这个for循环主要干两件事情,第一事判断closing and opening APP token是否是壁纸目标窗口;第二件事是找到最顶部(全屏窗口)的Activity窗口,取得窗口属性mAttrs的值,其实这个地方有四种情况:全屏Activity-->全屏Activity、全屏Activity-->非全屏Activity、非全屏Activity-->全屏Activity、非全屏Activity-->非全屏Activity,这四种情况使得animLp值会有所差异; final AppWindowToken wtoken; if (i < closingAppsCount) { wtoken = mClosingApps.valueAt(i); if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) { closingAppHasWallpaper = true; } } else { wtoken = mOpeningApps.valueAt(i - closingAppsCount); if (wtoken == lowerWallpaperAppToken || wtoken == upperWallpaperAppToken) { openingAppHasWallpaper = true; } } voiceInteraction |= wtoken.voiceInteraction; if (wtoken.appFullscreen) { WindowState ws = wtoken.findMainWindow(); if (ws != null) { animLp = ws.mAttrs; bestAnimLayer = ws.mLayer; fullscreenAnim = true; } } else if (!fullscreenAnim) { WindowState ws = wtoken.findMainWindow(); if (ws != null) { if (ws.mLayer > bestAnimLayer) { animLp = ws.mAttrs; bestAnimLayer = ws.mLayer; } } } } mAnimateWallpaperWithTarget = false; if (closingAppHasWallpaper && openingAppHasWallpaper) { //下面这段逻辑都是根据壁纸目标窗口的各种情况重新设置指定的transit值; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!"); switch (transit) { case AppTransition.TRANSIT_ACTIVITY_OPEN: case AppTransition.TRANSIT_TASK_OPEN: case AppTransition.TRANSIT_TASK_TO_FRONT: transit = AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN; break; case AppTransition.TRANSIT_ACTIVITY_CLOSE: case AppTransition.TRANSIT_TASK_CLOSE: case AppTransition.TRANSIT_TASK_TO_BACK: transit = AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE; break; } if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit: " + AppTransition.appTransitionToString(transit)); } else if ((oldWallpaper != null) && !mOpeningApps.isEmpty() && !mOpeningApps.contains(oldWallpaper.mAppToken)) { // We are transitioning from an activity with // a wallpaper to one without. transit = AppTransition.TRANSIT_WALLPAPER_CLOSE; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: " + AppTransition.appTransitionToString(transit)); } else if (mWallpaperTarget != null && mWallpaperTarget.isVisibleLw()) { // We are transitioning from an activity without // a wallpaper to now showing the wallpaper transit = AppTransition.TRANSIT_WALLPAPER_OPEN; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: " + AppTransition.appTransitionToString(transit)); } else { mAnimateWallpaperWithTarget = true; } // If all closing windows are obscured, then there is // no need to do an animation. This is the case, for // example, when this transition is being done behind // the lock screen. if (!mPolicy.allowAppAnimationsLw()) { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Animations disallowed by keyguard or dream."); animLp = null; } AppWindowToken topOpeningApp = null; AppWindowToken topClosingApp = null; int topOpeningLayer = 0; int topClosingLayer = 0; // Process all applications animating in place if (transit == AppTransition.TRANSIT_TASK_IN_PLACE) { //如果transit == AppTransition.TRANSIT_TASK_IN_PLACE,这段逻辑不知道是干啥的。。。 // Find the focused window final WindowState win = findFocusedWindowLocked(getDefaultDisplayContentLocked()); if (win != null) { final AppWindowToken wtoken = win.mAppToken; final AppWindowAnimator appAnimator = wtoken.mAppAnimator; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now animating app in place " + wtoken); appAnimator.clearThumbnail(); appAnimator.animation = null; updateTokenInPlaceLocked(wtoken, transit); wtoken.updateReportedVisibilityLocked(); appAnimator.mAllAppWinAnimators.clear(); final int N = wtoken.allAppWindows.size(); for (int j = 0; j < N; j++) { appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator); } mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); mAnimator.mAnimating |= appAnimator.showAllWindowsLocked(); } } appsCount = mOpeningApps.size(); for (i = 0; i < appsCount; i++) { //这个for循环主要干两件事:①调用setTokenVisibilityLocked()设置Activity切换动画;②寻找topOpeningApp值; AppWindowToken wtoken = mOpeningApps.valueAt(i); final AppWindowAnimator appAnimator = wtoken.mAppAnimator; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now opening app" + wtoken); if (!appAnimator.usingTransferredAnimation) { appAnimator.clearThumbnail(); appAnimator.animation = null; } wtoken.inPendingTransaction = false; if (!setTokenVisibilityLocked( //setTokenVisibilityLocked()函数主要依据第2,3,4,6个参数来决定加载哪个动画; wtoken, animLp, true, transit, false, voiceInteraction)){ // This token isn't going to be animating. Add it to the list of tokens to // be notified of app transition complete since the notification will not be // sent be the app window animator. mNoAnimationNotifyOnTransitionFinished.add(wtoken.token); } wtoken.updateReportedVisibilityLocked(); wtoken.waitingToShow = false; appAnimator.mAllAppWinAnimators.clear(); final int windowsCount = wtoken.allAppWindows.size(); for (int j = 0; j < windowsCount; j++) { appAnimator.mAllAppWinAnimators.add(wtoken.allAppWindows.get(j).mWinAnimator); } mAnimator.mAnimating |= appAnimator.showAllWindowsLocked(); //将Activity窗口状态从READY_TO_SHOW更新到HAS_DRAWN.该函数会调用WindowStateAnimator.performShowLocked().可能有细心的人发现了WindowStateAnimator.performShowLocked()函数会调用WindowStateAnimator.applyEnterAnimationLocked()设置窗口动画,但实际上是无法加载出Animation的,读者可将日志开关打开,然后在日志中搜索关键字“applyAnimation: win=”,查看Activity窗口的“a=”是否为null。对于非Activity窗口,WindowStateAnimator.applyEnterAnimationLocked()是可以为其加载出一个Animation的,当然得系统或窗口指定资源样式才行。现在可以总结出:对于Activity窗口是不可能在WindowStateAnimator.applyEnterAnimationLocked()成功设置一个窗口动画的,非Activity窗口才可以,比如PopupWindow、startingWindow、StatusBar、输入法窗口等。 mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); if (animLp != null) { int layer = -1; for (int j = 0; j < wtoken.windows.size(); j++) { WindowState win = wtoken.windows.get(j); if (win.mWinAnimator.mAnimLayer > layer) { layer = win.mWinAnimator.mAnimLayer; } } if (topOpeningApp == null || layer > topOpeningLayer) { topOpeningApp = wtoken; topOpeningLayer = layer; } } } appsCount = mClosingApps.size(); for (i = 0; i < appsCount; i++) { //这个for循环主要干两件事:①调用setTokenVisibilityLocked()设置Activity切换动画;②寻找topClosingApp值; AppWindowToken wtoken = mClosingApps.valueAt(i); final AppWindowAnimator appAnimator = wtoken.mAppAnimator; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Now closing app " + wtoken); appAnimator.clearThumbnail(); appAnimator.animation = null; wtoken.inPendingTransaction = false; setTokenVisibilityLocked(wtoken, animLp, false, transit, false, voiceInteraction); wtoken.updateReportedVisibilityLocked(); // Force the allDrawn flag, because we want to start // this guy's animations regardless of whether it's // gotten drawn. wtoken.allDrawn = true; wtoken.deferClearAllDrawn = false; // Ensure that apps that are mid-starting are also scheduled to have their // starting windows removed after the animation is complete if (wtoken.startingWindow != null && !wtoken.startingWindow.mExiting) { scheduleRemoveStartingWindowLocked(wtoken); } mAnimator.mAppWindowAnimating |= appAnimator.isAnimating(); if (animLp != null) { int layer = -1; for (int j = 0; j < wtoken.windows.size(); j++) { WindowState win = wtoken.windows.get(j); if (win.mWinAnimator.mAnimLayer > layer) { layer = win.mWinAnimator.mAnimLayer; } } if (topClosingApp == null || layer > topClosingLayer) { topClosingApp = wtoken; topClosingLayer = layer; } } } AppWindowAnimator openingAppAnimator = (topOpeningApp == null) ? null : topOpeningApp.mAppAnimator; AppWindowAnimator closingAppAnimator = (topClosingApp == null) ? null : topClosingApp.mAppAnimator; Bitmap nextAppTransitionThumbnail = mAppTransition.getNextAppTransitionThumbnail(); //上层应用调用了WMS.overridePendingAppTransitionAspectScaledThumb()或WMS.overridePendingAppTransitionThumb()进行覆盖Activity切换动画,下面这段代码就是这个覆盖动画逻辑处理; if (nextAppTransitionThumbnail != null && openingAppAnimator != null && openingAppAnimator.animation != null && nextAppTransitionThumbnail.getConfig() != Config.ALPHA_8) { // This thumbnail animation is very special, we need to have // an extra surface with the thumbnail included with the animation. Rect dirty = new Rect(0, 0, nextAppTransitionThumbnail.getWidth(), nextAppTransitionThumbnail.getHeight()); try { // TODO(multi-display): support other displays final DisplayContent displayContent = getDefaultDisplayContentLocked(); final Display display = displayContent.getDisplay(); final DisplayInfo displayInfo = displayContent.getDisplayInfo(); // Create a new surface for the thumbnail SurfaceControl surfaceControl = new SurfaceControl(mFxSession, "thumbnail anim", dirty.width(), dirty.height(), PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN); surfaceControl.setLayerStack(display.getLayerStack()); if (SHOW_TRANSACTIONS) { Slog.i(TAG, " THUMBNAIL " + surfaceControl + ": CREATE"); } // Draw the thumbnail onto the surface Surface drawSurface = new Surface(); drawSurface.copyFrom(surfaceControl); Canvas c = drawSurface.lockCanvas(dirty); c.drawBitmap(nextAppTransitionThumbnail, 0, 0, null); drawSurface.unlockCanvasAndPost(c); drawSurface.release(); // Get the thumbnail animation Animation anim; if (mAppTransition.isNextThumbnailTransitionAspectScaled()) { // For the new aspect-scaled transition, we want it to always show // above the animating opening/closing window, and we want to // synchronize its thumbnail surface with the surface for the // open/close animation (only on the way down) anim = mAppTransition.createThumbnailAspectScaleAnimationLocked( displayInfo.appWidth, displayInfo.appHeight, displayInfo.logicalWidth, transit); openingAppAnimator.thumbnailForceAboveLayer = Math.max(topOpeningLayer, topClosingLayer); openingAppAnimator.deferThumbnailDestruction = !mAppTransition.isNextThumbnailTransitionScaleUp(); } else { anim = mAppTransition.createThumbnailScaleAnimationLocked( displayInfo.appWidth, displayInfo.appHeight, transit); } anim.restrictDuration(MAX_ANIMATION_DURATION); anim.scaleCurrentDuration(getTransitionAnimationScaleLocked()); openingAppAnimator.thumbnail = surfaceControl; openingAppAnimator.thumbnailLayer = topOpeningLayer; openingAppAnimator.thumbnailAnimation = anim; openingAppAnimator.thumbnailX = mAppTransition.getStartingX(); openingAppAnimator.thumbnailY = mAppTransition.getStartingY(); } catch (OutOfResourcesException e) { Slog.e(TAG, "Can't allocate thumbnail/Canvas surface w=" + dirty.width() + " h=" + dirty.height(), e); openingAppAnimator.clearThumbnail(); } } mAppTransition.goodToGo(openingAppAnimator, closingAppAnimator); mAppTransition.postAnimationCallback(); mAppTransition.clear(); mOpeningApps.clear(); mClosingApps.clear(); // This has changed the visibility of windows, so perform // a new layout to get them all up-to-date. changes |= WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT | WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG; getDefaultDisplayContentLocked().layoutNeeded = true; // TODO(multidisplay): IMEs are only supported on the default display. if (windows == getDefaultWindowListLocked() && !moveInputMethodWindowsIfNeededLocked(true)) { assignLayersLocked(windows); } updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES, true /*updateInputWindows*/); mFocusMayChange = false; notifyActivityDrawnForKeyguard(); } return changes; }
上面这个函数很长,但是逻辑思路很简单,初看起来确实复杂,仔细分析发现还是比较容易理解的。总结起来就是:确定壁纸目标窗口对transit值的影响、取得顶层窗口的mAttr值、两次调用setTokenVisibilityLocked()来设置APPWindowToken可见性及加载对应的动画。
第11步、WMS.setTokenVisibilityLocked()
这个函数用来设置APPWindowToken.hidden的可见性、设置Activity切换动画(如果参数transit==AppTransition.TRANSIT_UNSET,那就是会设置窗口动画,否则就会设置Activity切换动画),如果存在Activity切换动画或属于该Activity的窗口正在做窗口动画,那么返回值为true。
boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp, boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) { boolean delayed = false; if (wtoken.clientHidden == visible) { //AppWindowToken.clientHidden保存着上层应用Activity窗口的可见性,通过AppWindowToken.sendAppVisibilityToClients()来通知上层应用窗口可见性。 wtoken.clientHidden = !visible; wtoken.sendAppVisibilityToClients(); } wtoken.willBeHidden = false; // Allow for state changes and animation to be applied if token is transitioning // visibility state or the token was marked as hidden and is exiting before we had a chance // to play the transition animation. if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting)) { //只有APPWindowToken.hidden的可见性切换时才进行重置其值及设置Activity切换动画或设置窗口动画; boolean changed = false; if (DEBUG_APP_TRANSITIONS) Slog.v( TAG, "Changing app " + wtoken + " hidden=" + wtoken.hidden + " performLayout=" + performLayout); boolean runningAppAnimation = false; //该变量用来确定是否设置了Activity切换动画,后面根据这个值来确定是否需要设置窗口动画; if (transit != AppTransition.TRANSIT_UNSET) { //如果transit != AppTransition.TRANSIT_UNSET表示指定了Activity切换动画,那么调用WMS.applyAnimationLocked()来加载Activity切换动画,同时把runningAppAnimation置为true,这样下面遍历窗口的时候便不会设置窗口动画; if (wtoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) { wtoken.mAppAnimator.animation = null; } if (applyAnimationLocked(wtoken, lp, transit, visible, isVoiceInteraction)) { //加载Activity切换动画,第十二步中会分析这个函数; delayed = runningAppAnimation = true; } WindowState window = wtoken.findMainWindow(); //TODO (multidisplay): Magnification is supported only for the default display. if (window != null && mAccessibilityController != null && window.getDisplayId() == Display.DEFAULT_DISPLAY) { mAccessibilityController.onAppWindowTransitionLocked(window, transit); } changed = true; } final int windowsCount = wtoken.allAppWindows.size(); for (int i = 0; i < windowsCount; i++) { //for循环遍历属于该APPWindowToken的所有窗口,一般情况下只有一个. WindowState win = wtoken.allAppWindows.get(i); if (win == wtoken.startingWindow) { //过滤startingWindow; continue; } //Slog.i(TAG, "Window " + win + ": vis=" + win.isVisible()); //win.dump(" "); if (visible) { //如果该APPWindowToken要切换为可见,并且所属的窗口当前是不可见状态,并且没有设置Activity切换动画,才设置窗口进入动画; if (!win.isVisibleNow()) { if (!runningAppAnimation) { win.mWinAnimator.applyAnimationLocked( WindowManagerPolicy.TRANSIT_ENTER, true); //WindowStateAnimator.applyAnimationLocked()函数分析见第二部分; //TODO (multidisplay): Magnification is supported only for the default if (mAccessibilityController != null && win.getDisplayId() == Display.DEFAULT_DISPLAY) { mAccessibilityController.onWindowTransitionLocked(win, WindowManagerPolicy.TRANSIT_ENTER); } } changed = true; final DisplayContent displayContent = win.getDisplayContent(); if (displayContent != null) { displayContent.layoutNeeded = true; } } } else if (win.isVisibleNow()) { //如果该APPWindowToken要切换为不可见,并且所属的窗口当前是可见状态,并且没有设置Activity切换动画,才设置窗口退出动画; if (!runningAppAnimation) { win.mWinAnimator.applyAnimationLocked( WindowManagerPolicy.TRANSIT_EXIT, false); //TODO (multidisplay): Magnification is supported only for the default if (mAccessibilityController != null && win.getDisplayId() == Display.DEFAULT_DISPLAY) { mAccessibilityController.onWindowTransitionLocked(win, WindowManagerPolicy.TRANSIT_EXIT); } } changed = true; final DisplayContent displayContent = win.getDisplayContent(); if (displayContent != null) { displayContent.layoutNeeded = true; } } } wtoken.hidden = wtoken.hiddenRequested = !visible; //设置APPWindowToken.hidden值; if (!visible) { unsetAppFreezingScreenLocked(wtoken, true, true); } else { // If we are being set visible, and the starting window is // not yet displayed, then make sure it doesn't get displayed. WindowState swin = wtoken.startingWindow; if (swin != null && !swin.isDrawnLw()) { //如果该APPWindowToken设为可见时,此时如果startingWindow还没显示,那么就无需显示startingWindow了,即将mPolicyVisibility置为false; swin.mPolicyVisibility = false; swin.mPolicyVisibilityAfterAnim = false; } } if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "setTokenVisibilityLocked: " + wtoken + ": hidden=" + wtoken.hidden + " hiddenRequested=" + wtoken.hiddenRequested); if (changed) { //changed=true代表上面有窗口可见性更改,那么就要更新输入法窗口在堆栈中的位置了; mInputMonitor.setUpdateInputWindowsNeededLw(); if (performLayout) { updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/); performLayoutAndPlaceSurfacesLocked(); } mInputMonitor.updateInputWindowsLw(false /*force*/); //将窗口堆栈信息更新到Input中去; } } if (wtoken.mAppAnimator.animation != null) { delayed = true; } for (int i = wtoken.allAppWindows.size() - 1; i >= 0 && !delayed; i--) { if (wtoken.allAppWindows.get(i).mWinAnimator.isWindowAnimating()) { delayed = true; } } return delayed; }
第12步、WMS.applyAnimationLocked()
该函数首先会根据AppWindowToken所属窗口是否是全屏窗口来确定containingFrame、contentInsets、appFrame三个值,然后调用AppTransition.loadAnimation()取得一个Activity切换动画Animation,最后将这个Animation通过AppTransition.setAnimation()接口设置到AppTransition中去。
private boolean applyAnimationLocked(AppWindowToken atoken, WindowManager.LayoutParams lp, int transit, boolean enter, boolean isVoiceInteraction) { // Only apply an animation if the display isn't frozen. If it is // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation // is running. if (okToDisplay()) { DisplayInfo displayInfo = getDefaultDisplayInfoLocked(); final int width = displayInfo.appWidth; final int height = displayInfo.appHeight; if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation: atoken=" + atoken); // Determine the visible rect to calculate the thumbnail clip WindowState win = atoken.findMainWindow(); Rect containingFrame = new Rect(0, 0, width, height); Rect contentInsets = new Rect(); Rect appFrame = new Rect(0, 0, width, height); if (win != null && win.isFullscreen(width, height)) { //只有窗口大于等于屏幕时,才会使用窗口的mContainingFrame、mContentInsets、mFrame值,这三个值会作为AppTransition.loadAnimation()的参数; // For fullscreen windows use the window frames and insets to set the thumbnail // clip. For none-fullscreen windows we use the app display region so the clip // isn't affected by the window insets. containingFrame.set(win.mContainingFrame); contentInsets.set(win.mContentInsets); appFrame.set(win.mFrame); } if (atoken.mLaunchTaskBehind) { // Differentiate the two animations. This one which is briefly on the screen // gets the !enter animation, and the other activity which remains on the // screen gets the enter animation. Both appear in the mOpeningApps set. enter = false; } Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height, mCurConfiguration.orientation, containingFrame, contentInsets, appFrame, isVoiceInteraction); //调用AppTransition.loadAnimation(),每个参数均会影响具体加载哪个动画,将在第十三步分析该函数; if (a != null) { if (DEBUG_ANIM) { RuntimeException e = null; if (!HIDE_STACK_CRAWLS) { e = new RuntimeException(); e.fillInStackTrace(); } Slog.v(TAG, "Loaded animation " + a + " for " + atoken, e); } atoken.mAppAnimator.setAnimation(a, width, height, //将前面加载的Animation设置到AppTransition中去; mAppTransition.canSkipFirstFrame()); } } else { atoken.mAppAnimator.clearAnimation(); } return atoken.mAppAnimator.animation != null; //如果成功设置Activity切换动画,那么该函数返回true; }
第13、14步、AppTransition.loadAnimation()/AppTransition.setAnimation()
AppTransition.loadAnimation()函数就是根据mNextAppTransitionType、transit、enter、isVoiceInteraction来决定出一个animAttr值,然后调用loadAnimationRes()或loadAnimationAttr()或其他创建Animation接口来加载一个Animation出来。现在很多应用使用startActivity(Intent intent, Bundle options)时在options参数中携带了自定义动画来取代系统默认Activity切换动画,那究竟是如何做到替换系统默认Activity切换动画的呢?秘密在于startActivity时,AMS会主动根据options携带的动画类型type,调用WMS.overridePendingAppTransition()等接口来覆盖Activity默认切换动画,当然上层应用也可直接调用WMS.overridePendingAppTransition()来覆盖Activity默认切换动画,关于startActivity()携带自定义Activity切换动画这个流程在文章第四部分会简单分析。
Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int appWidth, int appHeight, int orientation, Rect containingFrame, Rect contentInsets, Rect appFrame, boolean isVoiceInteraction) { Animation a; if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_OPEN || transit == TRANSIT_TASK_OPEN || transit == TRANSIT_TASK_TO_FRONT)) { //语音交互相关; a = loadAnimationRes(lp, enter ? com.android.internal.R.anim.voice_activity_open_enter : com.android.internal.R.anim.voice_activity_open_exit); if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation voice:" + " anim=" + a + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); } else if (isVoiceInteraction && (transit == TRANSIT_ACTIVITY_CLOSE || transit == TRANSIT_TASK_CLOSE || transit == TRANSIT_TASK_TO_BACK)) { //语音交互相关; a = loadAnimationRes(lp, enter ? com.android.internal.R.anim.voice_activity_close_enter : com.android.internal.R.anim.voice_activity_close_exit); if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation voice:" + " anim=" + a + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) { //调用过overridePendingAppTransition()接口设置动画。使用startActivity()携带ActivityOptions.ANIM_CUSTOM自定义Activity切换动画就是走这个逻辑的; a = loadAnimationRes(mNextAppTransitionPackage, enter ? mNextAppTransitionEnter : mNextAppTransitionExit); if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation:" + " anim=" + a + " nextAppTransition=ANIM_CUSTOM" + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM_IN_PLACE) { //调用过overrideInPlaceAppTransition()接口设置动画; a = loadAnimationRes(mNextAppTransitionPackage, mNextAppTransitionInPlace); if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation:" + " anim=" + a + " nextAppTransition=ANIM_CUSTOM_IN_PLACE" + " transit=" + appTransitionToString(transit) + " Callers=" + Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL) { a = createClipRevealAnimationLocked(transit, enter, appFrame); if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation:" + " anim=" + a + " nextAppTransition=ANIM_CLIP_REVEAL" + " Callers=" + Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_SCALE_UP) { //调用过overridePendingAppTransitionScaleUp()接口设置动画; a = createScaleUpAnimationLocked(transit, enter, appWidth, appHeight); if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation:" + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP" + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP || mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) { //调用过overridePendingAppTransitionThumb()接口设置动画; mNextAppTransitionScaleUp = (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP); a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter), appWidth, appHeight, transit); if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { String animName = mNextAppTransitionScaleUp ? "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN"; Slog.v(TAG, "applyAnimation:" + " anim=" + a + " nextAppTransition=" + animName + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); } } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP || mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN) { //调用过overridePendingAppTransitionAspectScaledThumb()接口设置动画; mNextAppTransitionScaleUp = (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP); a = createAspectScaledThumbnailEnterExitAnimationLocked( getThumbnailTransitionState(enter), appWidth, appHeight, orientation, transit, containingFrame, contentInsets); if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) { String animName = mNextAppTransitionScaleUp ? "ANIM_THUMBNAIL_ASPECT_SCALE_UP" : "ANIM_THUMBNAIL_ASPECT_SCALE_DOWN"; Slog.v(TAG, "applyAnimation:" + " anim=" + a + " nextAppTransition=" + animName + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); } } else { //这个就是普通Activity切换动画选择逻辑啦,根据transit、enter组合计算出一个animAttr值; int animAttr = 0; switch (transit) { case TRANSIT_ACTIVITY_OPEN: animAttr = enter ? WindowAnimation_activityOpenEnterAnimation : WindowAnimation_activityOpenExitAnimation; break; case TRANSIT_ACTIVITY_CLOSE: animAttr = enter ? WindowAnimation_activityCloseEnterAnimation : WindowAnimation_activityCloseExitAnimation; break; case TRANSIT_TASK_OPEN: animAttr = enter ? WindowAnimation_taskOpenEnterAnimation : WindowAnimation_taskOpenExitAnimation; break; case TRANSIT_TASK_CLOSE: animAttr = enter ? WindowAnimation_taskCloseEnterAnimation : WindowAnimation_taskCloseExitAnimation; break; case TRANSIT_TASK_TO_FRONT: animAttr = enter ? WindowAnimation_taskToFrontEnterAnimation : WindowAnimation_taskToFrontExitAnimation; break; case TRANSIT_TASK_TO_BACK: animAttr = enter ? WindowAnimation_taskToBackEnterAnimation : WindowAnimation_taskToBackExitAnimation; break; case TRANSIT_WALLPAPER_OPEN: animAttr = enter ? WindowAnimation_wallpaperOpenEnterAnimation : WindowAnimation_wallpaperOpenExitAnimation; break; case TRANSIT_WALLPAPER_CLOSE: animAttr = enter ? WindowAnimation_wallpaperCloseEnterAnimation : WindowAnimation_wallpaperCloseExitAnimation; break; case TRANSIT_WALLPAPER_INTRA_OPEN: animAttr = enter ? WindowAnimation_wallpaperIntraOpenEnterAnimation : WindowAnimation_wallpaperIntraOpenExitAnimation; break; case TRANSIT_WALLPAPER_INTRA_CLOSE: animAttr = enter ? WindowAnimation_wallpaperIntraCloseEnterAnimation : WindowAnimation_wallpaperIntraCloseExitAnimation; break; case TRANSIT_TASK_OPEN_BEHIND: animAttr = enter ? WindowAnimation_launchTaskBehindSourceAnimation : WindowAnimation_launchTaskBehindTargetAnimation; } a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null; //调用loadAnimationAttr()加载动画; if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation:" + " anim=" + a + " animAttr=0x" + Integer.toHexString(animAttr) + " transit=" + appTransitionToString(transit) + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3)); } return a; }从上面可以看到对于普通Activity切换,会调用AppTransition.loadAnimationAttr()来加载一个动画。
Animation loadAnimationAttr(WindowManager.LayoutParams lp, int animAttr) { int anim = 0; Context context = mContext; if (animAttr >= 0) { AttributeCache.Entry ent = getCachedAnimations(lp); if (ent != null) { context = ent.context; anim = ent.array.getResourceId(animAttr, 0); } } if (anim != 0) { return AnimationUtils.loadAnimation(context, anim); } return null; }
第15步、WMS.scheduleAnimationLocked()
触发动画下一帧计算逻辑。Activity切换动画、窗口动画究竟是如何实现的?说直白点就是调用scheduleAnimationLocked()后,使得在下一个vsync信号来临时触发调用计算窗口透明度、尺寸、旋转角度等值,然后将这些值设置到SurfaceFlinger中去。如果这一帧动画计算完成发现动画仍未结束,便会再次调用scheduleAnimationLocked()使得下一个vsync信号来临时重复之前的计算工作,依次往复,便达实现了想要的动画效果。至于这个一帧动画计算逻辑是如何执行的,请看下面第三部分。
第二部分:窗口动画设置
窗口动画的设置没那么复杂,我们就看一个比较常见的设置途径,流程如下。窗口动画设置关键函数在WindowStateAnimator.applyAnimationLocked()中,其他函数在第一部分已经分析过了,下面将重点分析这个函数。
WindowStateAnimator.applyAnimationLocked()函数对于Activity窗口来说并不会为其设置一个窗口动画,而对于非Activity窗口来说其窗口动画设置就是在这个函数中完成的,关于这一点已经在上面第一部分中的第十步WMS.handleAppTransitionReadyLocked()函数分析中讲的非常详细了,在此不多说。而对于非Activity窗口,比如popupwindow,可通过setAnimationStyle(int id)方法设置一个指定的样式窗口动画,样式如下:
<style name="popwin_anim_style"> <item name="android:windowEnterAnimation">@anim/menushow</item> <item name="android:windowExitAnimation">@anim/menuhide</item></style>
下面将详细分析WindowStateAnimator.applyAnimationLocked()函数。
boolean applyAnimationLocked(int transit, boolean isEntrance) { if ((mLocalAnimating && mAnimationIsEntrance == isEntrance) || mKeyguardGoingAwayAnimation) { //mLocalAnimating=true表示该窗口正在做动画。如果该窗口将要设置的动画(进入或退出)当前已经正在做,那么直接返回。 // If we are trying to apply an animation, but already running // an animation of the same type, then just leave that one alone. // If we are in a keyguard exit animation, and the window should animate away, modify // keyguard exit animation such that it also fades out. if (mAnimation != null && mKeyguardGoingAwayAnimation && transit == WindowManagerPolicy.TRANSIT_PREVIEW_DONE) { applyFadeoutDuringKeyguardExitAnimation(); } return true; } // Only apply an animation if the display isn't frozen. If it is // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation // is running. if (mService.okToDisplay()) { int anim = mPolicy.selectAnimationLw(mWin, transit); //调用PhoneWindowManager.selectAnimationLw(),如果该窗口是StatusBar、NavigationBar或startingWindow,那么该函数会返回一个指定的动画资源id; int attr = -1; Animation a = null; if (anim != 0) { a = anim != -1 ? AnimationUtils.loadAnimation(mContext, anim) : null; } else { switch (transit) { //根据transit值选择对应的样式id; case WindowManagerPolicy.TRANSIT_ENTER: attr = com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation; break; case WindowManagerPolicy.TRANSIT_EXIT: attr = com.android.internal.R.styleable.WindowAnimation_windowExitAnimation; break; case WindowManagerPolicy.TRANSIT_SHOW: attr = com.android.internal.R.styleable.WindowAnimation_windowShowAnimation; break; case WindowManagerPolicy.TRANSIT_HIDE: attr = com.android.internal.R.styleable.WindowAnimation_windowHideAnimation; break; } if (attr >= 0) { a = mService.mAppTransition.loadAnimationAttr(mWin.mAttrs, attr); //调用AppTransition.loadAnimationAttr()加载Animation; } } if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG, "applyAnimation: win=" + this + " anim=" + anim + " attr=0x" + Integer.toHexString(attr) + " a=" + a + " transit=" + transit + " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3)); if (a != null) { //对于Activity窗口,a=null,对于非Activity窗口,如果指定了窗口动画,那么a!=null; if (WindowManagerService.DEBUG_ANIM) { RuntimeException e = null; if (!WindowManagerService.HIDE_STACK_CRAWLS) { e = new RuntimeException(); e.fillInStackTrace(); } Slog.v(TAG, "Loaded animation " + a + " for " + this, e); } setAnimation(a); //将Animation设置到WindowStateAnimator.mAnimation中; mAnimationIsEntrance = isEntrance; } } else { clearAnimation(); } return mAnimation != null; }
如果Activity窗口调用此函数设置一个窗口动画,而从前面第一部分知道对于Activity会设置一个Activity切换动画,那这两个动画搅在一起岂不是乱套了。
对于WindowStateAnimator.applyAnimationLocked()函数还有其他地方会调用,下面调用栈就是一个例子。
at com.android.server.wm.WindowStateAnimator.applyAnimationLocked(WindowStateAnimator.java:1947)at com.android.server.wm.WindowManagerService.removeWindowLocked(WindowManagerService.java:3022)at com.android.server.wm.WindowManagerService.removeWindow(WindowManagerService.java:2977)at com.android.server.wm.Session.remove(Session.java:193)at android.view.IWindowSession$Stub.onTransact(IWindowSession.java:233)at com.android.server.wm.Session.onTransact(Session.java:136)
第三部分:动画每一帧计算
动画每一帧计算本质上就是根据第一部分加载出来的Animation(可能是Activity切换动画或窗口动画),结合时间变量计算出Transformation值,然后将Transformation值apply到窗口Surface的各个属性上,最后将窗口Surface属性设置到SurfaceFlinger中,完成动画一帧显示。下图就是动画一帧计算的时序图,下面将分析每一步调用。
第1、2步、WMS.performLayoutAndPlaceSurfacesLockedInner()
该函数是界面刷新函数,是WMS最核心的函数,窗口堆栈管理、各种特殊窗口处理、窗口layout计算等等均在该函数中完成,该函数最后会调用scheduleAnimationLocked()触发下一帧动画计算。scheduleAnimationLocked()只是简单的往Choreographer添加一个callback回调,待下一个vsync信号来临时便可触发调用callback。
void scheduleAnimationLocked() { if (!mAnimationScheduled) { mAnimationScheduled = true; mChoreographer.postFrameCallback(mAnimator.mAnimationFrameCallback); //mAnimationFrameCallback是一个Choreographer.FrameCallback对象,在WindowAnimator类构造函数中赋值; } }
WindowAnimator(final WindowManagerService service) { mService = service; mContext = service.mContext; mPolicy = service.mPolicy; mAnimationFrameCallback = new Choreographer.FrameCallback() { public void doFrame(long frameTimeNs) { synchronized (mService.mWindowMap) { mService.mAnimationScheduled = false; animateLocked(frameTimeNs); //调用父类animateLocked()函数; } } }; }
第3步、WindowAnimator.animateLocked()
该函数掌管着Activity切换动画、窗口动画、转屏动画,乃动画执行核心函数。从前面的动画设置部分我们已经知道,WMS会综合选择一个动画进入退出类型,然后根据resId从xml资源中加载出一个Animation对象,对于Activity切换动画、窗口动画会分别保存在AppWindowAnimator.animation和WindowStateAnimator.mAnimation中。WindowAnimator.animateLocked()函数会首先会根据Animation与时间值计算出一个Transformation值,Transformation中记录动画每一帧的透明度、裁剪尺寸、3*3阶旋转和缩放矩阵,然后在遍历窗口堆栈中所有窗口,调用WindowStateAnimator.prepareSurfaceLocked()来将Transformation值落实到更具体的surface属性上面,并设置到SurfaceFlinger中去,完成一帧动画的显示。
private void animateLocked(long frameTimeNs) { if (!mInitialized) { //对于默认显示设备准备好后,该mInitialized就会一直为true; return; } mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS; //将时间从纳秒折算成毫秒; mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE; boolean wasAnimating = mAnimating; mAnimating = false; //将mAnimating和mAppWindowAnimating重置为false。mAnimating是理解动画框架的关键,如果这一帧动画执行完成发现仍需接着执行下一帧动画,那么mAnimating就会被置为true。mAppWindowAnimating是AndroidM新增的一个变量,用来完善handleAnimatingStoppedAndTransitionLocked()被调用逻辑,AndroidM之前的版本在窗口做完动画也会触发调用handleAnimatingStoppedAndTransitionLocked(),AndroidM变成了只有在Activity切换动画完成时才会触发调用handleAnimatingStoppedAndTransitionLocked()。mAppWindowAnimating会在handleAppTransitionReadyLocked()中设置Activity切换动画时就置为true。 mAppWindowAnimating = false; if (WindowManagerService.DEBUG_WINDOW_TRACE) { Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime); } if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i( TAG, ">>> OPEN TRANSACTION animateLocked"); SurfaceControl.openTransaction(); //打开一次跟SurfaceFlinger的事务传输,待后面调用SurfaceControl.closeTransaction()时,会将这中间所有对surface的设置一次性传递给SurfaceFlinger,这样达到减少频繁进程间通信带来的损耗。 SurfaceControl.setAnimationTransaction(); try { final int numDisplays = mDisplayContentsAnimators.size(); for (int i = 0; i < numDisplays; i++) { //所有动画的计算就在这个for循环中,实际上这个只会循环一次,除非有多个显示设备。 final int displayId = mDisplayContentsAnimators.keyAt(i); updateAppWindowsLocked(displayId); //计算Activity切换动画Transformation值; DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i); final ScreenRotationAnimation screenRotationAnimation = displayAnimator.mScreenRotationAnimation; if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) { if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) { //计算转屏动画Transformation值; mAnimating = true; } else { mBulkUpdateParams |= SET_UPDATE_ROTATION; screenRotationAnimation.kill(); displayAnimator.mScreenRotationAnimation = null; //TODO (multidisplay): Accessibility supported only for the default display. if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) { // We just finished rotation animation which means we did not // anounce the rotation and waited for it to end, announce now. mService.mAccessibilityController.onRotationChangedLocked( mService.getDefaultDisplayContentLocked(), mService.mRotation); } } } // Update animations of all applications, including those // associated with exiting/removed apps updateWindowsLocked(displayId); //计算窗口动画Transformation值; updateWallpaperLocked(displayId); final WindowList windows = mService.getWindowListLocked(displayId); final int N = windows.size(); for (int j = 0; j < N; j++) {//遍历所有窗口,根据Transformation值,计算更新窗口的surface属性; windows.get(j).mWinAnimator.prepareSurfaceLocked(true); } } for (int i = 0; i < numDisplays; i++) { final int displayId = mDisplayContentsAnimators.keyAt(i); testTokenMayBeDrawnLocked(displayId); final ScreenRotationAnimation screenRotationAnimation = mDisplayContentsAnimators.valueAt(i).mScreenRotationAnimation; if (screenRotationAnimation != null) { screenRotationAnimation.updateSurfacesInTransaction(); } mAnimating |= mService.getDisplayContentLocked(displayId).animateDimLayers(); //TODO (multidisplay): Magnification is supported only for the default display. if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) { mService.mAccessibilityController.drawMagnifiedRegionBorderIfNeededLocked(); } } if (mAnimating) { //如果动画仍未结束,那么触发执行下一帧动画; mService.scheduleAnimationLocked(); } mService.setFocusedStackLayer(); if (mService.mWatermark != null) { mService.mWatermark.drawIfNeeded(); //如果打开了水印,重绘水印; } } catch (RuntimeException e) { Slog.wtf(TAG, "Unhandled exception in Window Manager", e); } finally { SurfaceControl.closeTransaction(); if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i( TAG, "<<< CLOSE TRANSACTION animateLocked"); } boolean hasPendingLayoutChanges = false; final int numDisplays = mService.mDisplayContents.size(); for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) { final DisplayContent displayContent = mService.mDisplayContents.valueAt(displayNdx); final int pendingChanges = getPendingLayoutChanges(displayContent.getDisplayId()); if ((pendingChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) { mBulkUpdateParams |= SET_WALLPAPER_ACTION_PENDING; } if (pendingChanges != 0) { hasPendingLayoutChanges = true; } } boolean doRequest = false; if (mBulkUpdateParams != 0) { doRequest = mService.copyAnimToLayoutParamsLocked(); } if (hasPendingLayoutChanges || doRequest) { mService.requestTraversalLocked(); } if (!mAnimating && wasAnimating) { mService.requestTraversalLocked(); } if (WindowManagerService.DEBUG_WINDOW_TRACE) { Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating + " mBulkUpdateParams=" + Integer.toHexString(mBulkUpdateParams) + " mPendingLayoutChanges(DEFAULT_DISPLAY)=" + Integer.toHexString(getPendingLayoutChanges(Display.DEFAULT_DISPLAY))); } }
第4、5、6步、WindowAnimator.updateAppWindowsLocked()
updateAppWindowsLocked()函数遍历所有AppWindowAnimator,调用AppWindowAnimator.stepAnimationLocked()函数,根据时间、Animation来更新AppWindowAnimator.transformation值。更具体的计算逻辑请自行分析Animation.getTransformation()函数,里面包含了插值器逻辑。
private void updateAppWindowsLocked(int displayId) { ArrayList<TaskStack> stacks = mService.getDisplayContentLocked(displayId).getStacks(); for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) { //for循环遍历所有Stack,我们知道在AMS中一个ActivityStack对应WMS中一个TaskStack,目前只有两个Stack; final TaskStack stack = stacks.get(stackNdx); final ArrayList<Task> tasks = stack.getTasks(); for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { //for循环遍历Stack中所有Task; final AppTokenList tokens = tasks.get(taskNdx).mAppTokens; for (int tokenNdx = tokens.size() - 1; tokenNdx >= 0; --tokenNdx) { final AppWindowAnimator appAnimator = tokens.get(tokenNdx).mAppAnimator; appAnimator.wasAnimating = appAnimator.animating; if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) { //调用AppWindowAnimator.stepAnimationLocked(),如果还有下一帧动画,那么函数返回true,该函数根据时间和插值器计算并更新AppWindowAnimator.transformation值; appAnimator.animating = true; mAnimating = mAppWindowAnimating = true; } else if (appAnimator.wasAnimating) { // stopped animating, do one more pass through the layout setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER, "appToken " + appAnimator.mAppToken + " done", displayId); if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG, "updateWindowsApps...: done animating " + appAnimator.mAppToken); } } } final AppTokenList exitingAppTokens = stack.mExitingAppTokens; //mExitingAppTokens中保存着延迟remove的AppWindowToken,为啥要延迟remove,因为需要一个退出动画或已经设置了一个退出动画; final int exitingCount = exitingAppTokens.size(); for (int i = 0; i < exitingCount; i++) { final AppWindowAnimator appAnimator = exitingAppTokens.get(i).mAppAnimator; appAnimator.wasAnimating = appAnimator.animating; if (appAnimator.stepAnimationLocked(mCurrentTime, displayId)) { //调用AppWindowAnimator.stepAnimationLocked(); mAnimating = mAppWindowAnimating = true; } else if (appAnimator.wasAnimating) { // stopped animating, do one more pass through the layout setAppLayoutChanges(appAnimator, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER, "exiting appToken " + appAnimator.mAppToken + " done", displayId); if (WindowManagerService.DEBUG_ANIM) Slog.v(TAG, "updateWindowsApps...: done animating exiting " + appAnimator.mAppToken); } } } }
转屏动画计算便在ScreenRotationAnimation.stepAnimationLocked()中完成,主要计算更新ScreenRotationAnimation.mEnterTransformation值。转屏动画的本质就是截一张图单独做转屏旋转,其Surface名为"ScreenshotSurface",也就是说转屏动画并不是通过控制应用窗口的surface属性来达到转屏效果的,而是单独创建了一个surface,该surface显示内容来自SurfaceControl.screenshot()接口,前面计算出来的mEnterTransformation等值通过控制这个单独surface的属性来达到转屏动画效果,关于转屏动画在此不多说,有兴趣的可以自行研究。但是有一个疑问,为啥做转屏动画时,比如从竖屏应用启动一个默认横屏的应用,系统并不会设置Activity切换动画?这个问题后面有空再分析解答。
第9、10、11步、WindowAnimator.updateWindowsLocked()
该函数会遍历所有窗口计算其窗口动画一帧值,即调用WindowStateAnimator.stepAnimationLocked()计算更新WindowStateAnimator.mTransformation值。其他逻辑主要涉及到keyguard显示后,某些窗口是否隐藏或显示,及keyguard退出动画等,在此不细细分析。
private void updateWindowsLocked(final int displayId) { ++mAnimTransactionSequence; final WindowList windows = mService.getWindowListLocked(displayId); if (mKeyguardGoingAway) { //mKeyguardGoingAway=true表示keyguard正在退出,那么对于窗口privateFlags & PRIVATE_FLAG_KEYGUARD!= 0窗口设置一个退出动画; for (int i = windows.size() - 1; i >= 0; i--) { WindowState win = windows.get(i); if (!mPolicy.isKeyguardHostWindow(win.mAttrs)) { continue; } final WindowStateAnimator winAnimator = win.mWinAnimator; if ((win.mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { if (!winAnimator.mAnimating) { if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: creating delay animation"); // Create a new animation to delay until keyguard is gone on its own. winAnimator.mAnimation = new AlphaAnimation(1.0f, 1.0f); winAnimator.mAnimation.setDuration(KEYGUARD_ANIM_TIMEOUT_MS); winAnimator.mAnimationIsEntrance = false; winAnimator.mAnimationStartTime = -1; winAnimator.mKeyguardGoingAwayAnimation = true; } } else { if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: StatusBar is no longer keyguard"); mKeyguardGoingAway = false; winAnimator.clearAnimation(); } break; } } mForceHiding = KEYGUARD_NOT_SHOWN; boolean wallpaperInUnForceHiding = false; boolean startingInUnForceHiding = false; ArrayList<WindowStateAnimator> unForceHiding = null; WindowState wallpaper = null; for (int i = windows.size() - 1; i >= 0; i--) { //for循环遍历堆栈中所有窗口; WindowState win = windows.get(i); WindowStateAnimator winAnimator = win.mWinAnimator; final int flags = win.mAttrs.flags; boolean canBeForceHidden = mPolicy.canBeForceHidden(win, win.mAttrs); //canBeForceHidden()对于TYPE_STATUS_BAR、TYPE_WALLPAPER等窗口返回false,对于其他类型窗口如位于TYPE_STATUS_BAR之下的,那么返回true; boolean shouldBeForceHidden = shouldForceHide(win); //shouldForceHide()返回true表示keyguard是显示状态,并且win必须被隐藏; if (winAnimator.mSurfaceControl != null) { final boolean wasAnimating = winAnimator.mWasAnimating; final boolean nowAnimating = winAnimator.stepAnimationLocked(mCurrentTime); //根据时间计算窗口动画Transition值,返回值为true表示还有下一帧动画,按理说该函数到这里就行了,为毛还有下面这一堆逻辑,下面来看下这些是干嘛的; winAnimator.mWasAnimating = nowAnimating; mAnimating |= nowAnimating; boolean appWindowAnimating = winAnimator.mAppAnimator != null && winAnimator.mAppAnimator.animating; boolean wasAppWindowAnimating = winAnimator.mAppAnimator != null && winAnimator.mAppAnimator.wasAnimating; boolean anyAnimating = appWindowAnimating || nowAnimating; //当前是否正在做Activity切换动画或窗口动画; boolean anyWasAnimating = wasAppWindowAnimating || wasAnimating; //前一次动画逻辑计算时,该Activity或窗口是否在做动画; try { if (anyAnimating && !anyWasAnimating) { //根据anyAnimating和anyWasAnimating组合判断当前是动画开始还是动画结束,进而通知上层应用窗口,这个是AndroidM新增的接口,非常棒的接口,AndroidM终于把动画这一块逻辑理清楚了。 win.mClient.onAnimationStarted(winAnimator.mAnimatingMove ? -1 : winAnimator.mKeyguardGoingAwayAnimation ? 1 : 0); } else if (!anyAnimating && anyWasAnimating) { win.mClient.onAnimationStopped(); } } catch (RemoteException e) { Slog.w(TAG, "Failed to dispatch window animation state change.", e); } if (WindowManagerService.DEBUG_WALLPAPER) { Slog.v(TAG, win + ": wasAnimating=" + wasAnimating + ", nowAnimating=" + nowAnimating); } if (wasAnimating && !winAnimator.mAnimating && mService.mWallpaperTarget == win) { //对于窗口动画结束时,如果该窗口是壁纸目标窗口,那么给pendingLayoutChanges加上FINISH_LAYOUT_REDO_WALLPAPER; mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE; setPendingLayoutChanges(Display.DEFAULT_DISPLAY, WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER); if (WindowManagerService.DEBUG_LAYOUT_REPEATS) { mService.debugLayoutRepeats("updateWindowsAndWallpaperLocked 2", getPendingLayoutChanges(Display.DEFAULT_DISPLAY)); } } //下面这些逻辑主要计算在keyguard状态下各种窗口可见性逻辑,及设置keyguard退出动画之类的,在此不细分析,省略; ........
第12步、WindowStateAnimator.prepareSurfaceLocked()
对窗口堆栈中所有窗口执行WindowStateAnimator.prepareSurfaceLocked()设置每个窗口的动画帧。在前面已经根据时间、Animation值将AppWindowAnimator.transformation和WindowStateAnimator.mTransformation计算出来,接着就该将这两个Transformation转换成Surface的属性并设置到SurfaceFlinger中去。
public void prepareSurfaceLocked(final boolean recoveringMemory) { final WindowState w = mWin; if (mSurfaceControl == null) { //对于没有Surface的窗口直接return,也就是说下面那些计算窗口surface显示相关的逻辑只针对有Surface的窗口; if (w.mOrientationChanging) { if (DEBUG_ORIENTATION) { Slog.v(TAG, "Orientation change skips hidden " + w); } w.mOrientationChanging = false; } return; } boolean displayed = false; computeShownFrameLocked(); //调用computeShownFrameLocked()计算Surface的mShownAlpha、mShownFrame,及缩放矩阵[mDsDx,mDtDx,mDsDy,mDtDy]; setSurfaceBoundariesLocked(recoveringMemory); //这个函数更新Surface的起始位置、大小、偏移缩放矩阵、clipRect区域。 if (mIsWallpaper && !mWin.mWallpaperVisible) { // Wallpaper is no longer visible and there is no wp target => hide it. hide(); } else if (w.mAttachedHidden || !w.isOnScreen()) { hide(); mService.hideWallpapersLocked(w); // If we are waiting for this window to handle an // orientation change, well, it is hidden, so // doesn't really matter. Note that this does // introduce a potential glitch if the window // becomes unhidden before it has drawn for the // new orientation. if (w.mOrientationChanging) { w.mOrientationChanging = false; if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change skips hidden " + w); } } else if (mLastLayer != mAnimLayer || mLastAlpha != mShownAlpha || mLastDsDx != mDsDx || mLastDtDx != mDtDx || mLastDsDy != mDsDy || mLastDtDy != mDtDy || w.mLastHScale != w.mHScale || w.mLastVScale != w.mVScale || mLastHidden) { displayed = true; mLastAlpha = mShownAlpha; mLastLayer = mAnimLayer; mLastDsDx = mDsDx; mLastDtDx = mDtDx; mLastDsDy = mDsDy; mLastDtDy = mDtDy; w.mLastHScale = w.mHScale; w.mLastVScale = w.mVScale; if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w, "alpha=" + mShownAlpha + " layer=" + mAnimLayer + " matrix=[" + mDsDx + "*" + w.mHScale + "," + mDtDx + "*" + w.mVScale + "][" + mDsDy + "*" + w.mHScale + "," + mDtDy + "*" + w.mVScale + "]", null); if (mSurfaceControl != null) { //更新surface的Alpha、Layer高度、2阶偏移缩放矩阵; try { mSurfaceAlpha = mShownAlpha; mSurfaceControl.setAlpha(mShownAlpha); mSurfaceLayer = mAnimLayer; mSurfaceControl.setLayer(mAnimLayer); mSurfaceControl.setMatrix( mDsDx * w.mHScale, mDtDx * w.mVScale, mDsDy * w.mHScale, mDtDy * w.mVScale); if (mLastHidden && mDrawState == HAS_DRAWN) { //前一次状态是隐藏,并且绘制状态是HAS_DRAWN绘制完成状态,那么调用showSurfaceRobustlyLocked()将窗口正真显示出来; if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w, "SHOW (performLayout)", null); if (WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG, "Showing " + w + " during relayout"); if (showSurfaceRobustlyLocked()) { mLastHidden = false; if (mIsWallpaper) { mService.dispatchWallpaperVisibility(w, true); } // This draw means the difference between unique content and mirroring. // Run another pass through performLayout to set mHasContent in the // LogicalDisplay. mAnimator.setPendingLayoutChanges(w.getDisplayId(), WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM); } else { w.mOrientationChanging = false; } } if (mSurfaceControl != null) { w.mToken.hasVisible = true; } } catch (RuntimeException e) { Slog.w(TAG, "Error updating surface in " + w, e); if (!recoveringMemory) { mService.reclaimSomeSurfaceMemoryLocked(this, "update", true); } } } } else { if (DEBUG_ANIM && isAnimating()) { Slog.v(TAG, "prepareSurface: No changes in animation for " + this); } displayed = true; } if (displayed) { if (w.mOrientationChanging) { if (!w.isDrawnLw()) { mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE; mAnimator.mLastWindowFreezeSource = w; if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation continue waiting for draw in " + w); } else { w.mOrientationChanging = false; if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change complete in " + w); } } w.mToken.hasVisible = true; } }
第13步、WindowStateAnimator.computeShownFrameLocked()
该函数根据转屏动画、窗口动画、父窗口动画、Activity切换动画的Transformation计算出Matrix矩阵值,及mShownFrame和mShownAlpha值,当然这些计算只针对有surface的窗口。
void computeShownFrameLocked() { final boolean selfTransformation = mHasLocalTransformation; //mHasLocalTransformation=true表示在准备或正在做窗口动画; Transformation attachedTransformation = (mAttachedWinAnimator != null && mAttachedWinAnimator.mHasLocalTransformation) ? mAttachedWinAnimator.mTransformation : null; //如果该窗口有父窗口,那么取得父窗口的Transformation值; Transformation appTransformation = (mAppAnimator != null && mAppAnimator.hasTransformation) ? mAppAnimator.transformation : null; //hasTransformation=true表示在准备货正在做Activity切换动画; // Wallpapers are animated based on the "real" window they // are currently targeting. final WindowState wallpaperTarget = mService.mWallpaperTarget; if (mIsWallpaper && wallpaperTarget != null && mService.mAnimateWallpaperWithTarget) { //这个逻辑基本上不会走,因为wallpaperTarget != null和mService.mAnimateWallpaperWithTarget基本不可能同时满足; final WindowStateAnimator wallpaperAnimator = wallpaperTarget.mWinAnimator; if (wallpaperAnimator.mHasLocalTransformation && wallpaperAnimator.mAnimation != null && !wallpaperAnimator.mAnimation.getDetachWallpaper()) { attachedTransformation = wallpaperAnimator.mTransformation; if (WindowManagerService.DEBUG_WALLPAPER && attachedTransformation != null) { Slog.v(TAG, "WP target attached xform: " + attachedTransformation); } } final AppWindowAnimator wpAppAnimator = wallpaperTarget.mAppToken == null ? null : wallpaperTarget.mAppToken.mAppAnimator; if (wpAppAnimator != null && wpAppAnimator.hasTransformation && wpAppAnimator.animation != null && !wpAppAnimator.animation.getDetachWallpaper()) { appTransformation = wpAppAnimator.transformation; if (WindowManagerService.DEBUG_WALLPAPER && appTransformation != null) { Slog.v(TAG, "WP target app xform: " + appTransformation); } } } final int displayId = mWin.getDisplayId(); final ScreenRotationAnimation screenRotationAnimation = mAnimator.getScreenRotationAnimationLocked(displayId); final boolean screenAnimation = screenRotationAnimation != null && screenRotationAnimation.isAnimating(); if (selfTransformation || attachedTransformation != null || appTransformation != null || screenAnimation) { //如果正在做窗口动画、父窗口有窗口动画、Activity切换动画、转屏动画中一种,那么需更新Surface属性。这个地方有个坑,为毛执行转屏动画时,无关窗口的Surface也被强制改了。 // cache often used attributes locally final Rect frame = mWin.mFrame; final float tmpFloats[] = mService.mTmpFloats; final Matrix tmpMatrix = mWin.mTmpMatrix; //下面这大段逻辑是根据转屏动画、窗口动画、父窗口动画、Activity切换动画计算一个tmpMatrix临时矩阵值; // Compute the desired transformation. if (screenAnimation && screenRotationAnimation.isRotating()) { // If we are doing a screen animation, the global rotation // applied to windows can result in windows that are carefully // aligned with each other to slightly separate, allowing you // to see what is behind them. An unsightly mess. This... // thing... magically makes it call good: scale each window // slightly (two pixels larger in each dimension, from the // window's center). final float w = frame.width(); final float h = frame.height(); if (w>=1 && h>=1) { tmpMatrix.setScale(1 + 2/w, 1 + 2/h, w/2, h/2); } else { tmpMatrix.reset(); } } else { tmpMatrix.reset(); } tmpMatrix.postScale(mWin.mGlobalScale, mWin.mGlobalScale); if (selfTransformation) { tmpMatrix.postConcat(mTransformation.getMatrix()); } tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset); if (attachedTransformation != null) { tmpMatrix.postConcat(attachedTransformation.getMatrix()); } if (appTransformation != null) { tmpMatrix.postConcat(appTransformation.getMatrix()); } if (screenAnimation) { tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix()); } //TODO (multidisplay): Magnification is supported only for the default display. if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) { MagnificationSpec spec = mService.mAccessibilityController .getMagnificationSpecForWindowLocked(mWin); if (spec != null && !spec.isNop()) { tmpMatrix.postScale(spec.scale, spec.scale); tmpMatrix.postTranslate(spec.offsetX, spec.offsetY); } } // "convert" it into SurfaceFlinger's format // (a 2x2 matrix + an offset) // Here we must not transform the position of the surface // since it is already included in the transformation. //Slog.i(TAG, "Transform: " + matrix); mHaveMatrix = true; tmpMatrix.getValues(tmpFloats); mDsDx = tmpFloats[Matrix.MSCALE_X]; //将tmpMatrix矩阵值分别保存在mDsDx、mDtDx、mDsDy、mDtDy中; mDtDx = tmpFloats[Matrix.MSKEW_Y]; mDsDy = tmpFloats[Matrix.MSKEW_X]; mDtDy = tmpFloats[Matrix.MSCALE_Y]; float x = tmpFloats[Matrix.MTRANS_X]; //(x,y)表示窗口在X、Y轴起始位置; float y = tmpFloats[Matrix.MTRANS_Y]; int w = frame.width(); int h = frame.height(); mWin.mShownFrame.set(x, y, x+w, y+h); // Now set the alpha... but because our current hardware // can't do alpha transformation on a non-opaque surface, // turn it off if we are running an animation that is also // transforming since it is more important to have that // animation be smooth. mShownAlpha = mAlpha; mHasClipRect = false; if (!mService.mLimitedAlphaCompositing || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format) || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy) && x == frame.left && y == frame.top))) { //将转屏动画、窗口动画、父窗口动画、Activity切换动画的Alpha值apply到mShownAlpha上; //Slog.i(TAG, "Applying alpha transform"); if (selfTransformation) { mShownAlpha *= mTransformation.getAlpha(); } if (attachedTransformation != null) { mShownAlpha *= attachedTransformation.getAlpha(); } if (appTransformation != null) { mShownAlpha *= appTransformation.getAlpha(); if (appTransformation.hasClipRect()) { mClipRect.set(appTransformation.getClipRect()); if (mWin.mHScale > 0) { mClipRect.left /= mWin.mHScale; mClipRect.right /= mWin.mHScale; } if (mWin.mVScale > 0) { mClipRect.top /= mWin.mVScale; mClipRect.bottom /= mWin.mVScale; } mHasClipRect = true; } } if (screenAnimation) { mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha(); } } else { //Slog.i(TAG, "Not applying alpha transform"); } if ((DEBUG_SURFACE_TRACE || WindowManagerService.localLOGV) && (mShownAlpha == 1.0 || mShownAlpha == 0.0)) Slog.v( TAG, "computeShownFrameLocked: Animating " + this + " mAlpha=" + mAlpha + " self=" + (selfTransformation ? mTransformation.getAlpha() : "null") + " attached=" + (attachedTransformation == null ? "null" : attachedTransformation.getAlpha()) + " app=" + (appTransformation == null ? "null" : appTransformation.getAlpha()) + " screen=" + (screenAnimation ? screenRotationAnimation.getEnterTransformation().getAlpha() : "null")); return; //注意这个return,有动画时在此返回; } else if (mIsWallpaper && mService.mInnerFields.mWallpaperActionPending) { return; } //下面这些逻辑都是无动画时会走的逻辑; if (WindowManagerService.localLOGV) Slog.v( TAG, "computeShownFrameLocked: " + this + " not attached, mAlpha=" + mAlpha); MagnificationSpec spec = null; //TODO (multidisplay): Magnification is supported only for the default display. if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) { spec = mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin); } if (spec != null) { //如果开启了系统访问控制,那么就会使用预设好的属性,一般情况这个是关闭的; final Rect frame = mWin.mFrame; final float tmpFloats[] = mService.mTmpFloats; final Matrix tmpMatrix = mWin.mTmpMatrix; tmpMatrix.setScale(mWin.mGlobalScale, mWin.mGlobalScale); tmpMatrix.postTranslate(frame.left + mWin.mXOffset, frame.top + mWin.mYOffset); if (spec != null && !spec.isNop()) { tmpMatrix.postScale(spec.scale, spec.scale); tmpMatrix.postTranslate(spec.offsetX, spec.offsetY); } tmpMatrix.getValues(tmpFloats); mHaveMatrix = true; mDsDx = tmpFloats[Matrix.MSCALE_X]; mDtDx = tmpFloats[Matrix.MSKEW_Y]; mDsDy = tmpFloats[Matrix.MSKEW_X]; mDtDy = tmpFloats[Matrix.MSCALE_Y]; float x = tmpFloats[Matrix.MTRANS_X]; float y = tmpFloats[Matrix.MTRANS_Y]; int w = frame.width(); int h = frame.height(); mWin.mShownFrame.set(x, y, x + w, y + h); mShownAlpha = mAlpha; } else { //没有动画,并且没有开启系统访问控制,那么就使用如下逻辑设定这些属性值; mWin.mShownFrame.set(mWin.mFrame); if (mWin.mXOffset != 0 || mWin.mYOffset != 0) { mWin.mShownFrame.offset(mWin.mXOffset, mWin.mYOffset); } mShownAlpha = mAlpha; mHaveMatrix = false; mDsDx = mWin.mGlobalScale; mDtDx = 0; mDsDy = 0; mDtDy = mWin.mGlobalScale; } }
这个函数更新Surface的起始位置、大小、偏移缩放矩阵、clipRect区域。
void setSurfaceBoundariesLocked(final boolean recoveringMemory) { final WindowState w = mWin; int width; int height; if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) { //目前只有SurfaceView设了这个flag; // for a scaled surface, we always want the requested // size. width = w.mRequestedWidth; height = w.mRequestedHeight; } else { width = w.mCompatFrame.width(); height = w.mCompatFrame.height(); } // Something is wrong and SurfaceFlinger will not like this, // try to revert to sane values if (width < 1) { width = 1; } if (height < 1) { height = 1; } float left = w.mShownFrame.left; float top = w.mShownFrame.top; // Adjust for surface insets. final LayoutParams attrs = w.getAttrs(); final int displayId = w.getDisplayId(); float scale = 1.0f; // Magnification is supported only for the default display. if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) { MagnificationSpec spec = mService.mAccessibilityController.getMagnificationSpecForWindowLocked(w); if (spec != null && !spec.isNop()) { scale = spec.scale; } } //mShownFrame的X、Y轴起始值和mCompatFrame的宽高还需加上一个LayoutParams.surfaceInsets矫正值; width += scale * (attrs.surfaceInsets.left + attrs.surfaceInsets.right); height += scale * (attrs.surfaceInsets.top + attrs.surfaceInsets.bottom); left -= scale * attrs.surfaceInsets.left; top -= scale * attrs.surfaceInsets.top; final boolean surfaceMoved = mSurfaceX != left || mSurfaceY != top; if (surfaceMoved) { //起始位置有偏移; mSurfaceX = left; mSurfaceY = top; try { if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w, "POS " + left + ", " + top, null); mSurfaceControl.setPosition(left, top); //更新surface的起始位置; } catch (RuntimeException e) { Slog.w(TAG, "Error positioning surface of " + w + " pos=(" + left + "," + top + ")", e); if (!recoveringMemory) { mService.reclaimSomeSurfaceMemoryLocked(this, "position", true); } } } final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height; if (surfaceResized) { //surface大小有更改; mSurfaceW = width; mSurfaceH = height; mSurfaceResized = true; try { if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w, "SIZE " + width + "x" + height, null); mSurfaceControl.setSize(width, height); //设置surface大小; mSurfaceControl.setMatrix( mDsDx * w.mHScale, mDtDx * w.mVScale, mDsDy * w.mHScale, mDtDy * w.mVScale); //设置surface的2阶偏移缩放矩阵。这个矩阵是如何用的?就是用这个二阶矩阵乘以(x,y)这个一阶矩阵,得出的坐标位置就是最终显示的坐标位置; mAnimator.setPendingLayoutChanges(w.getDisplayId(), WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER); if ((w.mAttrs.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) { final TaskStack stack = w.getStack(); if (stack != null) { stack.startDimmingIfNeeded(this); } } } catch (RuntimeException e) { // If something goes wrong with the surface (such // as running out of memory), don't take down the // entire system. Slog.e(TAG, "Error resizing surface of " + w + " size=(" + width + "x" + height + ")", e); if (!recoveringMemory) { mService.reclaimSomeSurfaceMemoryLocked(this, "size", true); } } } updateSurfaceWindowCrop(recoveringMemory); //调用updateSurfaceWindowCrop()设置surface的窗口切割区域,什么是窗口切割区域clipRect,比如图库中放大显示一张图片,这张图片大小大过屏幕尺寸,用户看到的区域就是切割区域大小,为啥要切割,因为用户看不到的区域没必要合成显示,SurfaceFlinger会根据这个clipRect区域进行合成;该函数调用SurfaceControl.setWindowCrop()来设置窗口clipRect区域。 }
第15、16步、SurfaceControl.setXXX()/showSurfaceRobustlyLocked()
这两步已经在上面的prepareSurfaceLocked()函数中分析过了,对showSurfaceRobustlyLocked()函数多说一句,该函数只有从不可见要变为可见状态时才会被调用,函数里面调用SurfaceControl.show()来通知SurfaceFlinger该窗口可以显示出来。
总结:
上面主要对每一步的函数调用进行了分析,动画每一帧计算就是由这些函数组成。希望读者要明白每个函数是干什么的,理解动画计算大概流程,待出问题时走一遍流程,找到问题点。动画每一帧计算主要可以概括成三步:第一步,将Animation根据时间值求出Transformation。第二步、根据计算出的Transformation值计算窗口Surface各个属性值。第三步、将Surface属性值设置到SurfaceFlinger中。
第四部分:通过日志快速分析动画资源来自哪里
分析步骤:
①首先打开WMS中所有日志开关,对于MTK平台,使用“adb shell dumpsys window -d enable a”便可打开WMS的日志开关。
②抓取日志,在日志中搜索“Setting animation in”关键字,定位到目标窗口日志行,再向上翻日志查到最近的“applyAnimation:”、“Loading animations: picked package=”和“Loading animations:”关键字;
③“Loading animations:”关键字
如果关键字是“Loading animations: layout params pkg=”,那么resId取自某个窗口的WindowManager.LayoutParams.windowAnimations值(可通过“adb shell dumpsys window w”打印窗口的属性查看究竟是哪个窗口的LayoutParams.windowAnimations值)。如果((resId&0xFF000000) == 0x01000000)为true,那么资源取自系统自带,否则取自应用程序。
如果关键字是"Loading animations: package=" ,那么该资源resId是应用程序调用overrideInPlaceAppTransition()或overridePendingAppTransition()接口传进来的,该resId自然取自应用程序。
③“Loading animations: picked package=”关键字
关键字后面接的就是这个动画资源来自哪里。如果"package = android"那这个可能是不准的,需要自己用((resId&0xFF000000) == 0x01000000)公式来算比较靠谱;如果package = 应用包名,那么资源就取自应用程序。
④“applyAnimation:”关键字
关键字可以判断出这个动画是什么类型的:如果后面接的是“win=”,那么就是窗口动画;如果后面接的是" anim=",那么就是Activity切换动画,对于Activity切换动画,这条日志可以推出更多的信息,比如transit值等;
下面是三份日志,第一份是设置Activity切换动画,第二份是设置PopupWindow窗口动画,第三份是设置Activity切换动画。读者可尝试根据上面所说的自行分析动画资源究竟取自哪里。
10-16 08:40:27.149 825 917 V AppTransition: Loading animations: package=com.meizu.flyme.launcher resId=0xa04004010-16 08:40:27.149 825 917 V AppTransition: Loading animations: picked package=com.meizu.flyme.launcher //关键字“Loading animations: picked package=”10-16 08:40:27.149 825 917 I AppTransition: id = a040040 overlayId = 010-16 08:40:27.151 825 917 V AppTransition: applyAnimation: anim=android.view.animation.AnimationSet@11689517 nextAppTransition=ANIM_CUSTOM transit=12 isEntrance=true Callers=com.android.server.wm.WindowManagerService.applyAnimationLocked:3878 com.android.server.wm.WindowManagerService.setTokenVisibilityLocked:4991 com.android.server.wm.WindowManagerService.handleAppTransitionReadyLocked:10053 10-16 08:40:27.151 825 917 V WindowManager: Loaded animation android.view.animation.AnimationSet@11689517 for AppWindowToken{1281e4b token=Token{234d731a ActivityRecord{1c78edc5 u0 com.android.mms/.ui.ConversationList t106}}}10-16 08:40:27.151 825 917 V WindowManager: java.lang.RuntimeException10-16 08:40:27.151 825 917 V WindowManager: at com.android.server.wm.WindowManagerService.applyAnimationLocked(WindowManagerService.java:3886)10-16 08:40:27.151 825 917 V WindowManager: at com.android.server.wm.WindowManagerService.setTokenVisibilityLocked(WindowManagerService.java:4991)10-16 08:40:27.151 825 917 V WindowManager: at com.android.server.wm.WindowManagerService.handleAppTransitionReadyLocked(WindowManagerService.java:10053)10-16 08:40:27.151 825 917 V WindowManager: at com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLockedInner(WindowManagerService.java:10788)10-16 08:40:27.151 825 917 V WindowManager: at com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLockedLoop(WindowManagerService.java:9601)10-16 08:40:27.151 825 917 V WindowManager: at com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLocked(WindowManagerService.java:9543)10-16 08:40:27.151 825 917 V WindowManager: at com.android.server.wm.WindowManagerService.access$700(WindowManagerService.java:182)10-16 08:40:27.151 825 917 V WindowManager: at com.android.server.wm.WindowManagerService$H.handleMessage(WindowManagerService.java:8457)10-16 08:40:27.151 825 917 V WindowManager: at android.os.Handler.dispatchMessage(Handler.java:111)10-16 08:40:27.151 825 917 V WindowManager: at android.os.Looper.loop(Looper.java:192)10-16 08:40:27.151 825 917 V WindowManager: at android.os.HandlerThread.run(HandlerThread.java:61)10-16 08:40:27.151 825 917 V WindowManager: at com.android.server.ServiceThread.run(ServiceThread.java:46) //“Setting animation in”关键字10-16 08:40:27.151 825 917 V AppWindowAnimator: Setting animation in AppWindowToken{1281e4b token=Token{234d731a ActivityRecord{1c78edc5 u0 com.android.mms/.ui.ConversationList t106}}}: android.view.animation.AnimationSet@11689517 wxh=1080x1920 isVisible=true10-16 08:40:27.151 825 917 V AppWindowAnimator: Updating layer Window{1e5b3c6c u0 Starting com.android.mms}: 22015
上面这份日志表明动画资源取自com.meizu.flyme.launcher。
10-16 16:48:12.874 825 1863 V AppTransition: Loading animations: layout params pkg=com.android.mms resId=0x7f0e008710-16 16:48:12.874 825 1863 V AppTransition: Loading animations: picked package=com.android.mms10-16 16:48:12.874 825 1863 I AppTransition: id = 7f050018 overlayId = 010-16 16:48:12.875 825 1863 V WindowStateAnimator: applyAnimation: win=WindowStateAnimator{8c23690 PopupWindow:2d5ab621} anim=0 attr=0x1 a=android.view.animation.AnimationSet@8928a66 transit=2 isEntrance=false Callers com.android.server.wm.WindowManagerService.removeWindowLocked:3022 com.android.server.wm.WindowManagerService.removeWindow:2977 com.android.server.wm.Session.remove:193 10-16 16:48:12.875 825 1863 V WindowStateAnimator: Loaded animation android.view.animation.AnimationSet@8928a66 for WindowStateAnimator{8c23690 PopupWindow:2d5ab621}10-16 16:48:12.875 825 1863 V WindowStateAnimator: java.lang.RuntimeException10-16 16:48:12.875 825 1863 V WindowStateAnimator: at com.android.server.wm.WindowStateAnimator.applyAnimationLocked(WindowStateAnimator.java:1947)10-16 16:48:12.875 825 1863 V WindowStateAnimator: at com.android.server.wm.WindowManagerService.removeWindowLocked(WindowManagerService.java:3022)10-16 16:48:12.875 825 1863 V WindowStateAnimator: at com.android.server.wm.WindowManagerService.removeWindow(WindowManagerService.java:2977)10-16 16:48:12.875 825 1863 V WindowStateAnimator: at com.android.server.wm.Session.remove(Session.java:193)10-16 16:48:12.875 825 1863 V WindowStateAnimator: at android.view.IWindowSession$Stub.onTransact(IWindowSession.java:233)10-16 16:48:12.875 825 1863 V WindowStateAnimator: at com.android.server.wm.Session.onTransact(Session.java:136)10-16 16:48:12.875 825 1863 V WindowStateAnimator: at android.os.Binder.execTransact(Binder.java:451)10-16 16:48:12.875 825 1863 V WindowStateAnimator: Setting animation in WindowStateAnimator{8c23690 PopupWindow:2d5ab621}: android.view.animation.AnimationSet@8928a66
上面这份日志表明动画资源取自com.android.mms。
10-16 16:58:18.414 825 917 V AppTransition: Loading animations: layout params pkg=android resId=0xa1000d410-16 16:58:18.414 825 917 V AppTransition: Loading animations: picked package=android10-16 16:58:18.414 825 917 I AppTransition: id = a04002d overlayId = 010-16 16:58:18.415 825 917 V AppTransition: applyAnimation: anim=android.view.animation.AnimationSet@185fc32b animAttr=0x4 transit=6 isEntrance=true Callers=com.android.server.wm.WindowManagerService.applyAnimationLocked:3878 com.android.server.wm.WindowManagerService.setTokenVisibilityLocked:4991 com.android.server.wm.WindowManagerService.handleAppTransitionReadyLocked:10053 10-16 16:58:18.416 825 917 V WindowManager: Loaded animation android.view.animation.AnimationSet@185fc32b for AppWindowToken{3583ca17 token=Token{987e96 ActivityRecord{233b49b1 u0 android/com.android.internal.app.MzResolverActivity t109}}}10-16 16:58:18.416 825 917 V WindowManager: java.lang.RuntimeException10-16 16:58:18.416 825 917 V WindowManager: at com.android.server.wm.WindowManagerService.applyAnimationLocked(WindowManagerService.java:3886)10-16 16:58:18.416 825 917 V WindowManager: at com.android.server.wm.WindowManagerService.setTokenVisibilityLocked(WindowManagerService.java:4991)10-16 16:58:18.416 825 917 V WindowManager: at com.android.server.wm.WindowManagerService.handleAppTransitionReadyLocked(WindowManagerService.java:10053)10-16 16:58:18.416 825 917 V WindowManager: at com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLockedInner(WindowManagerService.java:10788)10-16 16:58:18.416 825 917 V WindowManager: at com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLockedLoop(WindowManagerService.java:9601)10-16 16:58:18.416 825 917 V WindowManager: at com.android.server.wm.WindowManagerService.performLayoutAndPlaceSurfacesLocked(WindowManagerService.java:9543)10-16 16:58:18.416 825 917 V WindowManager: at com.android.server.wm.WindowManagerService.access$700(WindowManagerService.java:182)10-16 16:58:18.416 825 917 V WindowManager: at com.android.server.wm.WindowManagerService$H.handleMessage(WindowManagerService.java:8457)10-16 16:58:18.416 825 917 V WindowManager: at android.os.Handler.dispatchMessage(Handler.java:111)10-16 16:58:18.416 825 917 V WindowManager: at android.os.Looper.loop(Looper.java:192)10-16 16:58:18.416 825 917 V WindowManager: at android.os.HandlerThread.run(HandlerThread.java:61)10-16 16:58:18.416 825 917 V WindowManager: at com.android.server.ServiceThread.run(ServiceThread.java:46)10-16 16:58:18.416 825 917 V AppWindowAnimator: Setting animation in AppWindowToken{3583ca17 token=Token{987e96 ActivityRecord{233b49b1 u0 android/com.android.internal.app.MzResolverActivity t109}}}: android.view.animation.AnimationSet@185fc32b wxh=1080x1920 isVisible=true
最后总结一下:Activity切换动画可能来源包括系统默认、调用startActivity()或overridePendingAppTransition()指定、应用Activity内部重置窗口LayoutParams.windowAnimations值(可通过setTheme()来重置),这三者取其一,互斥关系;窗口动画来源包括:系统默认、窗口指定的LayoutParams.windowAnimations。
0 0
- WindowManagerService动画分析
- WindowManagerService动画分析
- WindowManagerService分析(草稿)
- Android窗口管理服务WindowManagerService显示窗口动画的原理分析
- Android窗口管理服务WindowManagerService显示窗口动画的原理分析
- android4.0 WindowManagerService的分析
- android4.0 WindowManagerService的分析
- WindowManagerService
- Android WindowManagerService的整体结构分析
- Android WindowManagerService机制分析(一):窗口的显示层级
- Android WindowManagerService
- WindowManagerService源码
- Android WindowManagerService
- Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析
- Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析
- Android窗口管理服务WindowManagerService对窗口的组织方式分析
- Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析
- Android窗口管理服务WindowManagerService对输入法窗口(Input Method Window)的管理分析
- jQuery Validate
- Maven 打包时报Fatal error compiling: tools.jar not found错误的问题处理
- linux项目发布常见命令
- Android.mk文件语法规范
- Android 自定义view实现水波纹效果
- WindowManagerService动画分析
- HDU5246 超级赛亚ACMer 排序+贪心+二分
- 数据包接收系列 — 上半部实现(网卡驱动)
- JAVA-反射-getGenericSuperclass()介绍
- 云服务列表
- 管理虚拟网络服务: 实践出真知
- what to do for the future?
- OpenGL 学习 显示列表
- 硬中断和软中断