美文网首页
WMS:窗口动画

WMS:窗口动画

作者: 81bad73e9053 | 来源:发表于2016-12-05 21:13 被阅读398次

动画的本质是:通过连续不断的显示若干个图像来产生动起来的效果,如平移动画就是在一定的时间段内,以恰当的速率每隔若干时间在屏幕上更新一次这个窗口的位置,缩放,旋转也都类似;任何动画效果都可以分为:窗口本身+特定时间点Matrix变换矩阵+一个代表Alpha变换的值###

1.窗口动画的类型

AppWindowAnimator
WindowStateAnimator
ScrennRotationAnimator

这三个动画类型所属的对象分别是:
AppWindowAnimator:属于AppWindowToken,它的成员变量mAppAnimator代表了此应用程序所属的AppWindowAnimator
WindowStateAnimator:WMS记录了所有窗口的WindowState,其中WindowState.mWinAnimator是一个WindowStateAnimator对象,它和上面AppWindowAnimator一样可以由开发人员定制。
ScrennRotationAnimator:屏幕旋转动画,WMS的mAnimator是一个WindowAnimator对象,WindowAnimator.mScreenRotationAnimator就是屏幕旋转动画。

1.1 相关常量

TRANSIT_ENTER:window被添加到屏幕上
TRANSIT_EXIT:window从屏幕移除
TRANSIT_SHOW:window变为可见
TRANSIT_HIDE:window变为不可见
TRANSIT_PREVIEW_DONE:starting window退出,以显示真正的窗口
TRANSIT_UNSET:未初始化
TRANSIT_NONE:没有设置动画
TRANSIT_ACTIVITY_OPEN:一个新的activity被同一个task中的另一个activity打开
TRANSIT_ACTIVITY_CLOSE:处于栈顶的activity关闭后,重新显示其下的activity,
TRANSIT_TASK_OPEN:activity在新的task被打开,属于ENTER动画
TRANSIT_TASK_CLOSE:activity关闭后,将显示前一个activity(不同的task栈)

2.WindowStateAnimator

2.1

public int relayoutWindow(......) { 
    //......
    if (toBeDisplayed) {
        if (win.isDrawnLw() && okToDisplay()) {
            winAnimator.applyEnterAnimationLocked();
        } 
}

当一个activity启动时,需要间接调用WMS提供的relayoutWindow来申请一个window,而且relayoutWindow既可以用来申请window也可以用来移除window,这取决于viewVisibility的设置,对于窗口进入动画viewVisibility取true

2.2

void applyEnterAnimationLocked() {
    final int transit;
    if (mEnterAnimationPending) {//窗口从无到有
        mEnterAnimationPending = false;
        transit = WindowManagerPolicy.TRANSIT_ENTER;
    } else {
        transit = WindowManagerPolicy.TRANSIT_SHOW;
    }
    //
    applyAnimationLocked(transit, true);
    
   
}


boolean applyAnimationLocked(int transit, boolean isEntrance) {
    if (mLocalAnimating && mAnimationIsEntrance == isEntrance) {
        return true;
    }
    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.loadAnimation(mWin.mAttrs, attr);
            }
        }
   
        if (a != null) { 
            setAnimation(a);//设置动画
            mAnimationIsEntrance = isEntrance;
        }
    } else {//屏幕不适合显示清除动画
        clearAnimation();
    } 
    return mAnimation != null;
}

1.获取对应的动画id
2.加载动画
加载动画使用的是AnimationUtils.loadAnimation
mService就是WMS,mAppTransaction就是AppTransaction对象

Animation loadAnimation(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);//获取动画anim
        }
    }
    if (anim != 0) {
        return AnimationUtils.loadAnimation(context, anim);//加载anim指定的动画资源
    }
    return null;
}

3.设置动画

设置并初始化,但是这是并没有真正的执行动画效果,动画是在VSYNC的组织下有序的动起来的

   public void setAnimation(Animation anim) {
     
        mAnimating = false;
        mLocalAnimating = false;
        mAnimation = anim;//把anim设置给mAnimation 
        mTransformation.setAlpha(mLastHidden ? 0 : 1);
        mHasLocalTransformation = true;
    } 

3.AppWindowAnimator

private boolean applyAnimationLocked(AppWindowToken atoken,
        WindowManager.LayoutParams lp, int transit, boolean enter) { 
    if (okToDisplay()) { 
        //加载动画资源
        Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height);
        if (a != null) {//设置动画 
            atoken.mAppAnimator.setAnimation(a, width, height);
        }
    } else {
        atoken.mAppAnimator.clearAnimation();
    } 
    return atoken.mAppAnimator.animation != null;
} 

4.动画的执行过程

4.1

WMS会在需要执行动画时通过scheduleAnimationLocked来设置一个触发源;mAnimationScheduled表示当前是否已经存在schedule animation,这个值在 mAnimationRunnable的run方法中被重新置为false,mChoreographer这个变量是线程单粒的;
android系统以VSYNC来刷新UI,那么应用程序是如何获取VSYNC信号呢?
这就是Choreographer(有序动作管理者),可以理解为VSYNC的接受者,VSYNC就是动画的触发源,Choreographer一方面接收VSYNC信号,另一方面将这一事件转发给感兴趣的人,所以希望监听VSYNC事件的对象都在Choreographer中注册。

TU 10-32

void scheduleAnimationLocked() {
    if (!mAnimationScheduled) {
        mAnimationScheduled = true;
        mChoreographer.postCallback(
                Choreographer.CALLBACK_ANIMATION, mAnimator.mAnimationRunnable, null);
    }
} 

4.2 当VSYNC信号产生之后,mAnimationRunnable的run函数被触发

WindowAnimator(final WindowManagerService service) {
    mService = service;
    mContext = service.mContext;
    mPolicy = service.mPolicy; 
    mAnimationRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (mService.mWindowMap) {
                mService.mAnimationScheduled = false;
                animateLocked();//动画执行  
            }
        }
    };
}

4.3 animateLocked

private void animateLocked() {
    if (!mInitialized) {//是否已经初始化
        return;
    }

    mCurrentTime = SystemClock.uptimeMillis();//当前时间
    mBulkUpdateParams = SET_ORIENTATION_CHANGE_COMPLETE;
    boolean wasAnimating = mAnimating;//记录上一次动画状态,用于判断动画是否结束
    mAnimating = false;//先置为false
  
    //业务开始,先记录所有对surface的更改,然后统一交给SurfaceFlinger
    SurfaceControl.openTransaction();
    SurfaceControl.setAnimationTransaction();
    try {// 执行App Window动画
        updateAppWindowsLocked();

        final int numDisplays = mDisplayContentsAnimators.size();
        for (int i = 0; i < numDisplays; i++) {
            //分别处理每个display中的动画
            final int displayId = mDisplayContentsAnimators.keyAt(i);
            DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
            final ScreenRotationAnimation screenRotationAnimation =
                    displayAnimator.mScreenRotationAnimation;
            if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
                if (screenRotationAnimation.stepAnimationLocked(mCurrentTime)) {
                    mAnimating = true;//动画还没有结束
                } else { 
                }
            }

            //更新WindowStateAnimator中的动画
            performAnimationsLocked(displayId);

            final WindowList windows = mService.getWindowListLocked(displayId);
            final int N = windows.size();
            for (int j = 0; j < N; j++) {
                //更新surface
                windows.get(j).mWinAnimator.prepareSurfaceLocked(true);
            }
        } 
    } catch (RuntimeException e) {
         
    } finally {
        //统一提交给SurfaceFlinger做处理
        SurfaceControl.closeTransaction(); 
    }
    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;
        }
    } 
    if (mAnimating) {
        //是否需要再执行动画,是的话就schedule下一次
        mService.scheduleAnimationLocked();
    } else if (wasAnimating) {//这是动画的最后一次单步
        mService.requestTraversalLocked();//要求traversal
        //发送一个DO_TRAVERSAL消息,随后performLayoutAndPlaceSurfacesLocked会再次被调用
    } 
} 

相关文章

网友评论

      本文标题:WMS:窗口动画

      本文链接:https://www.haomeiwen.com/subject/nedlmttx.html