android7.0动画,Android7.0窗口动画设置流程

Android的窗口动画可以分为三类

1.窗口本身的动画

2.被附加的窗口传递的动画(即父窗口的动画)

3.activity组件动画

根据WMS中的窗口动画设置流程,可以把12归为一类,即普通窗口动画,下面分别来讲一讲这两类窗口动画的设置流程

1.普通窗口动画的设置流程

WindowStateAnimator代表一个窗口动画对象,当一个窗口完成绘制之后会调用函数commitFinishDrawingLocked()

boolean commitFinishDrawingLocked() {

if (mDrawState != COMMIT_DRAW_PENDING && mDrawState != READY_TO_SHOW) {

return false;

}

mDrawState = READY_TO_SHOW; //设置当前绘制状态

boolean result = false;

final AppWindowToken atoken = mWin.mAppToken;

if (atoken == null || atoken.allDrawn || mWin.mAttrs.type == TYPE_APPLICATION_STARTING) {

result = performShowLocked();

}

return result;

}

如果该窗口动画对象对应的窗口不是一个activity组件窗口或者activity的窗口组件已经绘制完毕,就可以个给这个窗口设置一个窗口动画,即执行函数performShowLocked()

// This must be called while inside a transaction.

boolean performShowLocked() {

if (mWin.isHiddenFromUserLocked()) {

if (DEBUG_VISIBILITY) Slog.w(TAG, "hiding " + mWin + ", belonging to " + mWin.mOwnerUid);

mWin.hideLw(false);

return false;

}

mService.enableScreenIfNeededLocked();

applyEnterAnimationLocked();

// Force the show in the next prepareSurfaceLocked() call.

mLastAlpha = -1;

if (DEBUG_SURFACE_TRACE || DEBUG_ANIM)

Slog.v(TAG, "performShowLocked: mDrawState=HAS_DRAWN in " + mWin);

mDrawState = HAS_DRAWN;

mService.scheduleAnimationLocked();

int i = mWin.mChildWindows.size();

while (i > 0) {

i--;

WindowState c = mWin.mChildWindows.get(i);

if (c.mAttachedHidden) {

c.mAttachedHidden = false;

if (c.mWinAnimator.mSurfaceController != null) {

c.mWinAnimator.performShowLocked();

final DisplayContent displayContent = c.getDisplayContent();

if (displayContent != null) {

displayContent.layoutNeeded = true;

}

}

}

}

if (mWin.mAttrs.type != TYPE_APPLICATION_STARTING && mWin.mAppToken != null) {

mWin.mAppToken.onFirstWindowDrawn(mWin, this);

}

if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {

mWin.mDisplayContent.mDividerControllerLocked.resetImeHideRequested();

}

return true;

}

return false;

}

只有当mDrawState == READY_TO_SHOW的时候才能设置动画,继续看

void applyEnterAnimationLocked() {

// If we are the new part of a window replacement transition and we have requested

// not to animate, we instead want to make it seamless, so we don't want to apply

// an enter transition.

if (mWin.mSkipEnterAnimationForSeamlessReplacement) {

return;

}

final int transit;

if (mEnterAnimationPending) {

mEnterAnimationPending = false;

transit = WindowManagerPolicy.TRANSIT_ENTER;

} else {

transit = WindowManagerPolicy.TRANSIT_SHOW;

}

applyAnimationLocked(transit, true);

//TODO (multidisplay): Magnification is supported only for the default display.

if (mService.mAccessibilityController != null

&& mWin.getDisplayId() == DEFAULT_DISPLAY) {

mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit);

}

}

mEnterAnimationPending代表窗口处于等待显示的过程中,即从invisible变为visible,这时候就需要设置一个进入的动画,反之则设置一个退出的动画

boolean applyAnimationLocked(int transit, boolean isEntrance) {

if ((mLocalAnimating && mAnimationIsEntrance == isEntrance)

|| mKeyguardGoingAwayAnimation) {

// 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.

Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#applyAnimationLocked");

if (mService.okToDisplay()) {

int anim = mPolicy.selectAnimationLw(mWin, transit);// 能否创建动画

int attr = -1;

Animation a = null;

if (anim != 0) {

a = anim != -1 ? AnimationUtils.loadAnimation(mContext, anim) : null;// 可以创建,则加载具体动画

} else {

//得到动画样式

switch (transit) {

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);//根据动画样式加载动画

}

}

if (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) {

if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);

setAnimation(a);

mAnimationIsEntrance = isEntrance;

}

} else {

clearAnimation();

}

Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);

if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {

mService.adjustForImeIfNeeded(mWin.mDisplayContent);

if (isEntrance) {

mWin.setDisplayLayoutNeeded();

mService.mWindowPlacerLocked.requestTraversal();

}

}

return mAnimation != null;

}

具体根据样式来加载对应动画的细节不再详述,有兴趣可以查看源码

放一张时序图表示普通窗口动画的加载过程

image.png

2.activity组件动画的设置流程

前面一篇分析apptransition的文章说过,apptransition的最终步骤是调用windowSurfacePlacer的performSurfacePlacement函数来实现系统UI的刷新,同时设置并播放activity的组件动画,下面介绍一下这个过程

2.1系统UI刷新的主要流程

启动系统UI刷新的函数是定义在WindowSurfacePlacer.java中的requestTraversal(),这个函数进一步会执行performSurfacePlacement(),最终结果就是调用到performSurfacePlacementInner(),performSurfacePlacementInner() 是WMS服务中核心的部分,处理窗口属性变化,窗口排序,增加/删除窗口,以及窗口的切换和动画的显示等过程这里从app Transition入手,分析app Transition过程中的窗口动画设置以及显示的过程,其他内容暂不讨论

先看一下WindowSurfacePlacer中的几个关键函数

void requestTraversal() {

if (!mTraversalScheduled) {

mTraversalScheduled = true;

mService.mH.sendEmptyMessage(DO_TRAVERSAL);

}

}

final void performSurfacePlacement() {

if (mDeferDepth > 0) {

return;

}

int loopCount = 6;

do {

mTraversalScheduled = false;

performSurfacePlacementLoop();

mService.mH.removeMessages(DO_TRAVERSAL);

loopCount--;

} while (mTraversalScheduled && loopCount > 0);

mWallpaperActionPending = false;

}

private void performSurfacePlacementLoop() {

if (mInLayout) {

return;

}

......

mInLayout = true;

......

}

try {

performSurfacePlacementInner(recoveringMemory);

mInLayout = false;

if (mService.needsLayout()) {

if (++mLayoutRepeatCount < 6) {

requestTraversal();

} else {

mLayoutRepeatCount = 0;

}

} else {

mLayoutRepeatCount = 0;

}

......

} catch (RuntimeException e) {

mInLayout = false;

}

......

}

简单来说,系统UI刷新就是通过执行一个至多6次的while循环实现的,核心函数是performSurfacePlacementInner,这些函数执行前后会用一些标志位来保证不被重复调用到,可以用以下一张流程图表示

image.png

2.2activity组件动画设置

activity组件动画是在窗口刷新的时候设置的,performSurfacePlacementInner()在执行的时候会检查是否接下来要执行的apptransition操作是否已经准备好,是的话则会调用相关函数

private void performSurfacePlacementInner(boolean recoveringMemory) {

...

if (mService.mAppTransition.isReady()) {

defaultDisplay.pendingLayoutChanges |= handleAppTransitionReadyLocked(defaultWindows);

}

...

mService.scheduleAnimationLocked();

...

}

在设置动画之前还会对接下来要打开的activity组件进行检查,判断是否可以执行activity组件操作,只有结果为true才能设置切换动画。

同时还会把前面prepareAppTransition()时设置的APP_TRANSITION_TIMEOUT这个message从消息队列中移除后面就是分别对即将要打开或关闭的activity组件设置切换动画,我们继续分析这一过程

private int handleAppTransitionReadyLocked(WindowList windows) {

int appsCount = mService.mOpeningApps.size();

if (!transitionGoodToGo(appsCount)) {

return 0;

}

……

mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);

……

handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken);

……

final AppWindowToken topOpeningApp = handleOpeningApps(transit, animLp, voiceInteraction, topClosingLayer);

……

mService.mAppTransition.goodToGo(openingAppAnimator, closingAppAnimator, mService.mOpeningApps, mService.mClosingApps);

mService.mAppTransition.postAnimationCallback();

mService.mAppTransition.clear();

mService.mOpeningApps.clear();

mService.mClosingApps.clear();

……

return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_CONFIG;

}

handleClosingApps()和handleOpeningApps()的函数内容大致相同,以handleOpeningApps()为例

private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction, int topClosingLayer) {

AppWindowToken topOpeningApp = null;

final int appsCount = mService.mOpeningApps.size();

for (int i = 0; i < appsCount; i++) { //1. 遍历所有要打开的activity组件

AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);

final AppWindowAnimator appAnimator = wtoken.mAppAnimator;

if (!appAnimator.usingTransferredAnimation) {

appAnimator.clearThumbnail();

appAnimator.setNullAnimation(); //2. 清除原来的动画

}

wtoken.inPendingTransaction = false; //3. 表明对应的组件不是处于等待切换操作的状态

if (!mService.setTokenVisibilityLocked(wtoken, animLp, true, transit, false, voiceInteraction)){ // 4. 设置组件的可见性为true并设置切换动画

mService.mNoAnimationNotifyOnTransitionFinished.add(wtoken.token);

}

wtoken.updateReportedVisibilityLocked(); //5. 向AMS通知组件可见性

wtoken.waitingToShow = false; //6. 表示这个组件不是处于等待显示的状态

...

SurfaceControl.openTransaction();

...

}

return topOpeningApp;

}

具体设置组件切换动画是在设置窗口可见性的时候执行的,即setTokenVisibilityLocked函数

boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp,

boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {

boolean delayed = false;

...

boolean visibilityChanged = false;

if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting) || (visible && wtoken.waitingForReplacement())) {

boolean changed = false;

boolean runningAppAnimation = false;

if (transit != AppTransition.TRANSIT_UNSET) {

if (wtoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) {

wtoken.mAppAnimator.setNullAnimation();

}

if (applyAnimationLocked(wtoken, lp, transit, visible, isVoiceInteraction)) {

delayed = runningAppAnimation = true;

}

...

}

... // 没有设置合适的切换动画的话则遍历窗口单独设置窗口动画

...

}

最后设置动画的操作是通过调用函数applyAnimationLocked(),通过这一系列操作,一个合适的切换动画就被设置给了AppWindowToken对象

private boolean applyAnimationLocked(AppWindowToken atoken, WindowManager.LayoutParams lp, int transit, boolean enter, boolean isVoiceInteraction) {

if (okToDisplay()) {

...

Animation a = mAppTransition.loadAnimation(lp, transit, enter, mCurConfiguration.uiMode,

mCurConfiguration.orientation, frame, displayFrame, insets, surfaceInsets,

isVoiceInteraction, freeform, atoken.mTask.mTaskId);

if (a != null) {

final int containingWidth = frame.width();

final int containingHeight = frame.height();

atoken.mAppAnimator.setAnimation(a, containingWidth, containingHeight,

mAppTransition.canSkipFirstFrame(), mAppTransition.getAppStackClipMode());

}

} else {

atoken.mAppAnimator.clearAnimation();

}

...

return atoken.mAppAnimator.animation != null;

}

最终返回动画的函数细节定义在AppTransition.java中

Animation loadAnimation(...) {

Animation a;

if (...) {

...

} else {

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_DOCK_TASK_FROM_RECENTS:

case TRANSIT_TASK_OPEN:

animAttr = enter ? WindowAnimation_taskOpenEnterAnimation : WindowAnimation_taskOpenExitAnimation;

break;

...

}

a = animAttr != 0 ? loadAnimationAttr(lp, animAttr) : null;

...

}

return a;

}

再往后就是资源查找的过程,这里就不一一分析了,贴一张流程图表示以上过程

image.png

3.普通窗口动画与activity组件动画的关系

如果一个窗口属于activity组件,那么普通的窗口动画是会被activity组件给覆盖掉的,否则就执行普通的窗口动画,具体可以看到WindowStateAnimator.java中的定义,stackclip代表的窗口动画的优先级

private int resolveStackClip() {

// App animation overrides window animation stack clip mode.

if (mAppAnimator != null && mAppAnimator.animation != null) {

return mAppAnimator.getStackClip();

} else {

return mStackClip;

}

}

你可能感兴趣的:(android7.0动画)