二. 窗口动画的显示框架
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
private final void performLayoutAndPlaceSurfacesLockedInner(
boolean recoveringMemory) {
......
Surface.openTransaction();
......
try {
......
int repeats = 0;
int changes = 0;
do {
repeats++;
if (repeats > 6) {
......
break;}
// 1. 计算各个窗口的大小
// FIRST LOOP: Perform a layout, if needed.
if (repeats < 4) {
changes = performLayoutLockedInner();
if (changes != 0) {
continue;
}
} else {
Slog.w(TAG, "Layout repeat skipped after too many iterations");
changes = 0;
}
// 2. 推进各个Activity组件的切换动画
// Update animations of all applications, including those
// associated with exiting/removed apps
boolean tokensAnimating = false;
final int NAT = mAppTokens.size();
for (i=0; i<NAT; i++) {
if (mAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) {
tokensAnimating = true;
}
}
final int NEAT = mExitingAppTokens.size();
for (i=0; i<NEAT; i++) {
if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) {
tokensAnimating = true;
}
}
// 3. 推进各个窗口的动画
// SECOND LOOP: Execute animations and update visibility of windows.
......
animating = tokensAnimating;
......
mPolicy.beginAnimationLw(dw, dh);
final int N = mWindows.size();
for (i=N-1; i>=0; i--) {
WindowState w = mWindows.get(i);
......
if (w.mSurface != null) {
// Execute animation.
......
if (w.stepAnimationLocked(currentTime, dw, dh)) {
animating = true;
......
}
......
}
......
mPolicy.animatingWindowLw(w, attrs);
}
......
changes |= mPolicy.finishAnimationLw();
......
} while (changes != 0);
// 4. 更新各个窗口的绘图表面
// THIRD LOOP: Update the surfaces of all windows.
......
final int N = mWindows.size();
for (i=N-1; i>=0; i--) {
WindowState w = mWindows.get(i);
......
if (w.mSurface != null) {
......
//计算实际要显示的大小和位置
w.computeShownFrameLocked();
......
//设置大小和位置等
......
if (w.mAttachedHidden || !w.isReadyForDisplay()) {
......
} else if (w.mLastLayer != w.mAnimLayer
|| w.mLastAlpha != w.mShownAlpha
|| w.mLastDsDx != w.mDsDx
|| w.mLastDtDx != w.mDtDx
|| w.mLastDsDy != w.mDsDy
|| w.mLastDtDy != w.mDtDy
|| w.mLastHScale != w.mHScale
|| w.mLastVScale != w.mVScale
|| w.mLastHidden) {
......
//设置Z轴位置、Alpha通道和变换矩阵
......
if (w.mLastHidden && !w.mDrawPending
&& !w.mCommitDrawPending
&& !w.mReadyToShow) {
......
if (showSurfaceRobustlyLocked(w)) {
w.mHasDrawn = true;
w.mLastHidden = false;
}
......
}
......
}
......
}
......
}
......
} catch (RuntimeException e) {
......
}
......
Surface.closeTransaction();
......
// 5. 检查是否需要再一次刷新系统UI
if (needRelayout) {
requestAnimationLocked(0);
} else if (animating) {
requestAnimationLocked(currentTime+(1000/60)-SystemClock.uptimeMillis());
}
......
}
......
}
三. 窗口动画的推进过程
窗口动画可能是来自窗口本身所设置的动画,也有可能是来自其宿主activity组件所设置的切换动画
1.窗口动画的推进过程
窗口动画的推进过程是WindowState::stepAnimationLocked实现
窗口动画的推进只有以下四个条件均满足的情况下才会执行:
1)屏幕没有被冻结,mDisplayFrozen
2)屏幕是点亮的,isScreenOn为true
3)窗口的UI已经绘制完成并且已经提交,mDrawPending和mCommitDrawPending的值均为false
4)窗口已经被设置过动画,mAnimation != false
满足四个条件后,就会执行以下操作:
1)将mHasLocalTransformation设置为true,表明窗口当前具有一个本地动画
2)检查mLocalAnimating的值是否等于false。如果等于false,说明窗口动画还没有经过初始化,这时候就需要对该动画进行初始化,通过成员函数initialize和setStartTime来实现的。还需要将成员变量mLocalAnimating和mAnimating的值均设置为true,表明窗口动画已经初始化过了,并且窗口当前正在执行动画的过程中。
3)将mTransformation所描述的变换矩阵的数据清空。
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
private final class WindowState implements WindowManagerPolicy.WindowState {
......
// Currently running animation.
boolean mAnimating; //表示窗口是否处于正在显示动画的过程中
boolean mLocalAnimating;//表示窗口的动画是否已经初始化。一个动画只有经过初始化,才能开始执行
Animation mAnimation;//表示窗口的动画对象
......
boolean mHasLocalTransformation;//表示窗口的动画是否是一个本地动画,即这个动画是否是来自窗口本身。有时候一个窗口虽然正在显示动画,但是这个动画可能是其宿主activity组件的切换动画。
final Transformation mTransformation = new Transformation();//表示一个变换矩阵
......
// This must be called while inside a transaction. Returns true if
// there is more animation to run.
boolean stepAnimationLocked(long currentTime, int dw, int dh) {
if (!mDisplayFrozen && mPolicy.isScreenOn()) { //屏幕未被冻结 并且 是点亮状态
// We will run animations as long as the display isn't frozen.
if (!mDrawPending && !mCommitDrawPending && mAnimation != null) {
//UI已经绘制完成 并且已经提交并且已经设置过动画
......
mHasLocalTransformation = true;
if (!mLocalAnimating) {
......
mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh);
mAnimation.setStartTime(currentTime);
mLocalAnimating = true;
mAnimating = true;
}
mTransformation.clear();
final boolean more = mAnimation.getTransformation(
currentTime, mTransformation);
......
if (more) { //动画尚未结束显示
// we're not done!
return true;
}
......
}
......
}
......
mAnimating = false;
mLocalAnimating = false;
mAnimation = null;
......
mHasLocalTransformation = false;
......
mTransformation.clear();
......
return false;
}
......
}
......
}
2.activity组件切换动画的推进过程
activity组件切换动画的推进过程是由AppWindowToken::stepAnimationLocked来实现
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
class AppWindowToken extends WindowToken {
......
boolean animating;//已经初始化
Animation animation;//Activity组件的切换动画对象
boolean hasTransformation;//是否具有切换动画
final Transformation transformation = new Transformation();
//表示一个变换矩阵,根据切换动画的当前推进状态来计算得到的,用来改变组件窗口的大小以及位置
......
// This must be called while inside a transaction.
boolean stepAnimationLocked(long currentTime, int dw, int dh) {
if (!mDisplayFrozen && mPolicy.isScreenOn()) {/没有被冻结 并且 点亮状态
// We will run animations as long as the display isn't frozen.
if (animation == sDummyAnimation) {//若两者指向的对象是同一个,说明是哑动画
// This guy is going to animate, but not yet. For now count
// it as not animating for purposes of scheduling transactions;
// when it is really time to animate, this will be set to
// a real animation and the next call will execute normally.
return false;}
if ((allDrawn || animating || startingDisplayed) && animation != null) {
//其中一个值为true,说明所有窗口UI已经绘制完成 并且 组件已经被设置过切换动画
if (!animating) {//未进行初始化
......
animation.initialize(dw, dh, dw, dh);
animation.setStartTime(currentTime);
animating = true;
}
transformation.clear();
final boolean more = animation.getTransformation(
currentTime, transformation);
......
if (more) {
// we're done!
hasTransformation = true;
return true;
}
......
animation = null;
}
}
hasTransformation = false;
......
animating = false;
......
transformation.clear();
......
......
}
......
}
return false;
}
四.窗口动画的合成过程
从之前的内容可以知道,WindowState类的成员函数computeShownFrameLocked负责合成窗口的动画,包括窗口本身所设置的进入(退出)动画,从被附加窗口传递过来的动画,以及宿主activity的切换动画。窗口的这三个动画合成之后,就可以得到一个变换矩阵。将这个变换矩阵应用到窗口的原始大小和位置上,就可以得到窗口经过动画变换后所得到的位置和大小。
WindowManagerService::computeShownFrameLocked
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor {
......
private final class WindowState implements WindowManagerPolicy.WindowState {
......
// Actual frame shown on-screen (may be modified by animation)
final Rect mShownFrame = new Rect();//用来描述窗口当前所要显示的位置和大小
......
// Current transformation being applied.
float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1;//用来描述窗口的变换矩阵(二维)
......
// "Real" frame that the application sees.
final Rect mFrame = new Rect();//用来描述窗口的实际位置和大小
......
void computeShownFrameLocked() {
final boolean selfTransformation = mHasLocalTransformation;//表示窗口本身设置有一个动画,要么是打开窗口类型的动画,要么是一个关闭窗口类型的动画
Transformation attachedTransformation =
//当前窗口是附加在另外一个窗口之上 && 被附加的窗口也设置有动画
(mAttachedWindow != null && mAttachedWindow.mHasLocalTransformation)
? mAttachedWindow.mTransformation : null;
Transformation appTransformation =
//不等于null,说明当前正在处理的窗口是一个activity组件的窗口
//并且这个组件设置有切换动画
(mAppToken != null && mAppToken.hasTransformation)
? mAppToken.transformation : null;
// Wallpapers are animated based on the "real" window they
// are currently targeting.
//检查当前正在处理的窗口是否是一个壁纸窗口,即TYPE_WALLPAPER是否等于1
//如果当前正在处理的窗口是一个壁纸窗口,并且它有且仅有一个目标窗口
if (mAttrs.type == TYPE_WALLPAPER && mLowerWallpaperTarget == null
&& mWallpaperTarget != null) {
//以下要对壁纸窗口的动画进行特殊处理,要把壁纸窗口所附加在的窗口的动画设置为壁纸窗口的目标窗口所附加在的窗口的动画
//1. 要把壁纸窗口所附加在的窗口的动画设置为壁纸窗口的目标窗口所附加在的窗口的动画,即将变量attachedTransformation指向用来描述壁纸窗口的目标窗口所附加在的窗口当前所要执行的动画的一个变换矩阵,前提是壁纸窗口的目标窗口设置有动画,并且这个目标窗口在结束动画过程后不会与壁纸窗口分离。
if (mWallpaperTarget.mHasLocalTransformation &&
mWallpaperTarget.mAnimation != null &&
!mWallpaperTarget.mAnimation.getDetachWallpaper()) {
attachedTransformation = mWallpaperTarget.mTransformation;
......
}
//2. 要把壁纸窗口的切换动画设置为壁纸窗口的目标窗口的切换动画,即将变量appTransformation指向用来描述壁纸窗口的目标窗口当前所要执行的切换动画的一个变换矩阵,前提是壁纸窗口的目标窗口设置有切换动画,并且这个目标窗口结束动画过程后不会与壁纸窗口分离。
if (mWallpaperTarget.mAppToken != null &&
mWallpaperTarget.mAppToken.hasTransformation &&
mWallpaperTarget.mAppToken.animation != null &&
!mWallpaperTarget.mAppToken.animation.getDetachWallpaper()) {
appTransformation = mWallpaperTarget.mAppToken.transformation;
......
}}
if (selfTransformation || attachedTransformation != null
|| appTransformation != null) { //说明当前正在处理的窗口有动画需要显示,接下来就要将这些动画组合成一个总的变换矩阵
// cache often used attributes locally
final Rect frame = mFrame;
final float tmpFloats[] = mTmpFloats;
final Matrix tmpMatrix = mTmpMatrix;
// Compute the desired transformation.
tmpMatrix.setTranslate(0, 0);//将该矩阵的偏移位置初始化为 0,0
if (selfTransformation) {//当前正在处理的窗口本身设置有动画
tmpMatrix.postConcat(mTransformation.getMatrix());//将两个矩阵相乘,就可以得到一个中间变换矩阵
}
tmpMatrix.postTranslate(frame.left, frame.top);//将中间变换矩阵的偏移位置设置为当前正在处理窗口的实际位置
if (attachedTransformation != null) {//当前正处理的窗口宿主activity组件设置有切换动画
tmpMatrix.postConcat(attachedTransformation.getMatrix());
}
if (appTransformation != null) {
tmpMatrix.postConcat(appTransformation.getMatrix());
}
// "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);
//最终得到的变换矩阵保存在变量tmpMatrix中,由于需要设置到SurfaceFlinger服务中,因此需要转换
tmpMatrix.getValues(tmpFloats);
mDsDx = tmpFloats[Matrix.MSCALE_X];
mDtDx = tmpFloats[Matrix.MSKEW_X];
mDsDy = tmpFloats[Matrix.MSKEW_Y];
mDtDy = tmpFloats[Matrix.MSCALE_Y];
int x = (int)tmpFloats[Matrix.MTRANS_X] + mXOffset;
int y = (int)tmpFloats[Matrix.MTRANS_Y] + mYOffset;
int w = frame.width();
int h = frame.height();
mShownFrame.set(x, y, x+w, y+h);
......
return;
}
//如果当前正在处理的窗口没有动画显示,即变量selfTransformation的值等于false,并且变量attachedTransformation和appTransformation的值均等于null
//只要简单地将成员变量mFrame的内容设置到成员变量mShownFrame中,
并且将成员变量mDsDx、mDtDx、mDsDy和mDtDy分别设置为1、0、0和1即可,表示当前正在处理的窗口既没有切变变换,也没有缩放变换。
mShownFrame.set(mFrame);
if (mXOffset != 0 || mYOffset != 0) {
mShownFrame.offset(mXOffset, mYOffset);
}
......
mDsDx = 1;
mDtDx = 0;
mDsDy = 0;
mDtDy = 1;
}
......
}
......
}