分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
在前一文中,我们分析了Activity组件的切换过程。从这个过程可以知道,所有参与切换操作的窗口都会被设置切换动画。事实上,一个窗口在打开(关闭)的过程中,除了可能会设置切换动画之外,它本身也可能会设置有进入(退出)动画。再进一步地,如果一个窗口是附加在另外一个窗口之上的,那么被附加窗口所设置的动画也会同时传递给该窗口。本文就详细分析WindowManagerService服务显示窗口动画的原理。
《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!
在Android系统中,窗口动画的本质就是对原始窗口施加一个变换(Transformation)。在线性数学中,对物体的形状进行变换是通过乘以一个矩阵(Matrix)来实现,目的就是对物体进行偏移、旋转、缩放、切变、反射和投影等。因此,给窗口设置动画实际上就给窗口设置一个变换矩阵(Transformation Matrix)。
如前所述,一个窗口在打开(关闭)的过程,可能会被设置三个动画,它们分别是窗口本身所设置的进入(退出)动画(Self Transformation)、从被附加窗口传递过来的动画(Attached Transformation),以及宿主Activity组件传递过来的切换动画(App Transformation)。这三个Transformation组合在一起形成一个变换矩阵,以60fps的速度应用在窗口的原始形状之上,完成窗口的动画过程,如图1所示。
图1 窗口的动画显示过程
从上面的分析可以知道,窗口的变换矩阵是应用在窗口的原始位置和大小之上的,因此,在显示窗口的动画之前,除了要给窗口设置变换矩阵之外,还要计算好窗口的原始位置和大小,以及布局和绘制好窗口的UI。在前面Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析和Android应用程序窗口(Activity)的测量(Measure)、布局(Layout)和绘制(Draw)过程分析这两篇文章中,我们已经分析过窗口的位置和大小计算过程以及窗口UI的布局和绘制过程了,本文主要关注窗口动画的设置、合成和显示过程。这三个过程通过以下四个部分的内容来描述:
1. 窗口动画的设置过程
2. 窗口动画的显示框架
3. 窗口动画的推进过程
4. 窗口动画的合成过程
其中,窗口动画的设置过程包括上述三个动画的设置过程,窗口动画的推进过程是指定动画的一步一步地迁移的过程,窗口动画的合成过程是指上述三个动画组合成一个变换矩阵的过程,后两个过程包含在了窗口动画的显示框架中。
一. 窗口动画的设置过程
窗口被设置的动画虽然可以达到三个,但是这三个动画可以归结为两类,一类是普通动画,例如,窗口在打开过程中被设置的进入动画和在关闭过程中被设置的退出动画,另一类是切换动画。其中,Self Transformation和Attached Transformation都是属于普通动画,而App Transformation属于切换动画。接下来我们就分别分析这两种类型的动画的设置过程。
1. 普通动画的设置过程
从前面Android窗口管理服务WindowManagerService显示Activity组件的启动窗口(Starting Window)的过程分析一文可以知道,窗口在打开的过程中,是通过调用WindowState类的成员函数performShowLocked来实现的,如下所示:
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private final class WindowState implements WindowManagerPolicy.WindowState { ...... boolean performShowLocked() { ...... if (mReadyToShow && isReadyForDisplay()) { ...... if (!showSurfaceRobustlyLocked(this)) { return false; } ...... applyEnterAnimationLocked(this); ...... } return true; } ...... } ...... }
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowState类的成员函数performShowLocked首先是调用WindowManagerService类的成员函数showSurfaceRobustlyLocked来通知SurfaceFlinger服务将当前正在处理的窗口设置为可见,接着再调用WindowManagerService类的成员函数applyEnterAnimationLocked来给当前正在处理的窗口设置一个进入动画,如下所示:
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private void applyEnterAnimationLocked(WindowState win) { int transit = WindowManagerPolicy.TRANSIT_SHOW; if (win.mEnterAnimationPending) { win.mEnterAnimationPending = false; transit = WindowManagerPolicy.TRANSIT_ENTER; } applyAnimationLocked(win, transit, true); } ......}
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
如果参数win所指向的一个WindowState对象的成员变量mEnterAnimationPending的值等于true,那么就说明它所描述的窗口正在等待显示,也就是正处于不可见到可见状态的过程中,那么WindowManagerService类的成员函数applyEnterAnimationLocked就会对该窗口设置一个类型为WindowManagerPolicy.TRANSIT_ENTER的动画,否则的话,就会对该窗口设置一个类型为WindowManagerPolicy.TRANSIT_SHOW的动画。
确定好窗口的动画类型之后,WindowManagerService类的成员函数applyEnterAnimationLocked就调用另外一个成员函数applyAnimationLocked来为窗口创建一个动画了。接下来我们先分析窗口在关闭的过程中所设置的动画类型,然后再来分析WindowManagerService类的成员函数applyAnimationLocked的实现。
从前面Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析一文可以知道,当应用程序进程请求WindowManagerService服务刷新一个窗口的时候,会调用到WindowManagerService类的成员函数relayoutWindow。WindowManagerService类的成员函数relayoutWindow在执行的过程中,如果发现需要将一个窗口从可见状态设置为不可见状态时,也就是发现需要关闭一个窗口时,就会对该窗口设置一个退出动出,如下所示:
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... public int relayoutWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewVisibility, boolean insetsPending, Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { ...... synchronized(mWindowMap) { WindowState win = windowForClientLocked(session, client, false); ...... if (viewVisibility == View.VISIBLE && (win.mAppToken == null || !win.mAppToken.clientHidden)) { ...... } else { ...... if (win.mSurface != null) { ...... // If we are not currently running the exit animation, we // need to see about starting one. if (!win.mExiting || win.mSurfacePendingDestroy) { // Try starting an animation; if there isn't one, we // can destroy the surface right away. int transit = WindowManagerPolicy.TRANSIT_EXIT; ...... if (!win.mSurfacePendingDestroy && win.isWinVisibleLw() && applyAnimationLocked(win, transit, false)) { ...... win.mExiting = true; } ...... } ...... } ...... } ...... } ...... } ......}
这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。
WindowState对象win描述的便是要刷新的窗口。当参数viewVisibility的值不等于View.VISIBLE时,就说明要将WindowState对象win所描述的窗口设置为不可见。另一方面,如果WindowState对象win的成员变量mAppToken的值不等于null,并且这个成员变量所指向的一个AppWindowToken对象的成员变量clientHidden的值等于true,那么就说明WindowState对象win所描述的窗口是一个与Activity组件相关的窗口,并且该Activity组件是处于不可见状态的。在这种情况下,也需要将WindowState对象win所描述的窗口设置为不可见。
一旦WindowState对象win所描述的窗口要设置为不可见,就需要考虑给它设置一个退出动画,不过有四个前提条件:
1. 该窗口有一个绘图表面,即WindowState对象win的成员变量mSurface的值不等于null;
2. 该窗口的绘图表面不是处于等待销毁的状态,即WindowState对象win的成员变量mSurfacePendingDestroy的值不等于true;
3. 该窗口不是处于正在关闭的状态,即WindowState对象win的成员变量mExiting的值不等于true;
4. 该窗口当前正在处于可见的状态,即WindowState对象win的成员isWinVisibleLw的返回值等于true。
在满足上述四个条件的情况下,就说明WindowState对象win所描述的窗口的状态要由可见变为不可见,因此,就需要给它设置一个退出动画,即一个类型为WindowManagerPolicy.TRANSIT_EXIT的动画,这同样是通过调用WindowManagerService类的成员函数applyAnimationLocked来实现的。
从上面的分析就可以知道,无论是窗口在打开时所需要的进入动画,还是窗口在关闭时所需要的退出动画,都是通过调用WindowManagerService类的成员函数applyAnimationLocked来设置的,它的实现如下所示:
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private boolean applyAnimationLocked(WindowState win, int transit, boolean isEntrance) { if (win.mLocalAnimating && win.mAnimationIsEntrance == isEntrance) { // If we are trying to apply an animation, but already running // an animation of the same type, then just leave that one alone. 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 (!mDisplayFrozen && mPolicy.isScreenOn()) { int anim = mPolicy.selectAnimationLw(win, transit); int attr = -1; Animation a = null; if (anim != 0) { a = AnimationUtils.loadAnimation(mContext, anim); } 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 = loadAnimation(win.mAttrs, attr); } } ...... if (a != null) { ...... win.setAnimation(a); win.mAnimationIsEntrance = isEntrance; } } ...... return win.mAnimation != null; } ......}
这个函数定义在文件frameworks/base/services/ja