您现在的位置:首页 > 博客 > Android开发 > 正文
Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析
http://www.drovik.com/      2013-2-1 15:36:40      来源:老罗的Android之旅      点击:
 WindowManagerService类的成员函数performLayoutAndPlaceSurfacesLockedInner是一个巨无霸的函数,它一共有1200+行代码,承载了WindowManagerService服务的核心功能。对于这样一个巨无霸函数,要逐行地分析它的实现是很困难的,因为要理解各种上下文信息,才可以清楚地知道它的执行过程。这里我们就大概地分析它的实现框架,以后再逐步地分析它的具体实现:

        1. 在一个最多执行7次的while循环中,做两件事情:第一件事情是计算各个窗品的大小,这是通过调用另外一个成员函数performLayoutLockedInner来实现的;第二件事情是执行窗口的动画,主要是处理窗口的启动窗口显示动画和窗口切换过程中的动画,以及更新各个窗口的可见性。注意,每一次while循环执行之后,如果发现系统中的各个窗口的相应布局属性不再发生变化,那么就不行执行下一次的while循环了,即该while循环可能不用执行7次就结束了。窗口的动画显示过程和窗口的可见性更新过程是相当复杂的,它们也是WindowManagerService服务最为核的地方,在后面的文章中,我们再详细分析。

        2. 经过第1点的操作之后,接下来就可以将各个窗口的属性,例如,大小、位置等属性,通知SurfaceFlinger服务了,也就是让SurfaceFlinger服务更新它里面的各个Layer的属性值,以便可以对这些Layer执行可见性计算、合成等操作,最后渲染到硬件帧缓冲区中去。SurfaceFlinger服务计算系统中各个窗口,即各个Layer的可见性,以便将它们合成、渲染到硬件帧缓冲区的过程可以参考前面Android系统Surface机制的SurfaceFlinger服务渲染应用程序UI的过程分析一文。注意,各个窗口的属性更新操作是被包含在SurfaceFlinger服务的一个事务中的,即一个Transaction中,这样做是为了避免每更新一个窗口的一个属性就触发SurfaceFlinger服务重新计算各个Layer的可见性,以及对各个Layer进行合并和渲染的操作。启动SurfaceFlinger服务的一个事务可以通过调用Surface类的静态成员函数openTransaction来实现,而关闭SurfaceFlinger服务的一个事务可以通过调用Surface类的静态成员函数closeTransaction来实现。

       3. 经过第1点和第2点的操作之后,一次系统UI的刷新过程就完成了,这时候就会将系统中的那些不会再显示的窗口的绘图表面销毁掉,并且将那些已经完成退出了的窗口令牌,即将我们在前面Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析一文中所提到的WindowToken移除掉,以及将那些已经退出了的Activity窗口令牌,即将我们在前面Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析一文中所提到的AppWindowToken也移除掉。这一步实际执行的是窗口清理操作。

       上述三个操作是WindowManagerService类的成员函数performLayoutAndPlaceSurfacesLockedInner的实现关键所在,理解了这三个操作,基本也就可以理解WindowManagerService服务刷新系统UI的过程了。

       接下来,我们继续分析WindowManagerService类的成员函数performLayoutLockedInner的实现,以便可以继续了解Activity窗口的大小计算过程。

       Step 7. WindowManagerService.performLayoutLockedInner

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();  
  6.     ......  
  7.   
  8.     /** 
  9.      * Z-ordered (bottom-most first) list of all Window objects. 
  10.      */  
  11.     final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();  
  12.     ......  
  13.   
  14.     private final int performLayoutLockedInner() {  
  15.         ......  
  16.   
  17.         final int dw = mDisplay.getWidth();  
  18.         final int dh = mDisplay.getHeight();  
  19.   
  20.         final int N = mWindows.size();  
  21.         int i;  
  22.   
  23.         ......  
  24.   
  25.         mPolicy.beginLayoutLw(dw, dh);  
  26.   
  27.         int seq = mLayoutSeq+1;  
  28.         if (seq < 0) seq = 0;  
  29.         mLayoutSeq = seq;  
  30.   
  31.         // First perform layout of any root windows (not attached  
  32.         // to another window).  
  33.         int topAttached = -1;  
  34.         for (i = N-1; i >= 0; i--) {  
  35.             WindowState win = mWindows.get(i);  
  36.             ......  
  37.   
  38.             final AppWindowToken atoken = win.mAppToken;  
  39.             final boolean gone = win.mViewVisibility == View.GONE  
  40.                     || !win.mRelayoutCalled  
  41.                     || win.mRootToken.hidden  
  42.                     || (atoken != null && atoken.hiddenRequested)  
  43.                     || win.mAttachedHidden  
  44.                     || win.mExiting || win.mDestroying;  
  45.             ......  
  46.   
  47.             if (!gone || !win.mHaveFrame) {  
  48.                 if (!win.mLayoutAttached) {  
  49.                     mPolicy.layoutWindowLw(win, win.mAttrs, null);  
  50.                     win.mLayoutSeq = seq;  
  51.                     ......  
  52.                 } else {  
  53.                     if (topAttached < 0) topAttached = i;  
  54.                 }  
  55.             }  
  56.         }  
  57.         ......  
  58.   
  59.         for (i = topAttached; i >= 0; i--) {  
  60.             WindowState win = mWindows.get(i);  
  61.   
  62.             // If this view is GONE, then skip it -- keep the current  
  63.             // frame, and let the caller know so they can ignore it  
  64.             // if they want.  (We do the normal layout for INVISIBLE  
  65.             // windows, since that means "perform layout as normal,  
  66.             // just don't display").  
  67.             if (win.mLayoutAttached) {  
  68.                 ......  
  69.   
  70.                 if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled)  
  71.                         || !win.mHaveFrame) {  
  72.                     mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow);  
  73.                     win.mLayoutSeq = seq;  
  74.                     ......  
  75.                 }  
  76.             }  
  77.         }  
  78.   
  79.         ......  
  80.   
  81.         return mPolicy.finishLayoutLw();  
  82.     }  
  83.   
  84.     ......  
  85. }  
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager(); ...... /** * Z-ordered (bottom-most first) list of all Window objects. */ final ArrayList<WindowState> mWindows = new ArrayList<WindowState>(); ...... private final int performLayoutLockedInner() { ...... final int dw = mDisplay.getWidth(); final int dh = mDisplay.getHeight(); final int N = mWindows.size(); int i; ...... mPolicy.beginLayoutLw(dw, dh); int seq = mLayoutSeq+1; if (seq < 0) seq = 0; mLayoutSeq = seq; // First perform layout of any root windows (not attached // to another window). int topAttached = -1; for (i = N-1; i >= 0; i--) { WindowState win = mWindows.get(i); ...... final AppWindowToken atoken = win.mAppToken; final boolean gone = win.mViewVisibility == View.GONE || !win.mRelayoutCalled || win.mRootToken.hidden || (atoken != null && atoken.hiddenRequested) || win.mAttachedHidden || win.mExiting || win.mDestroying; ...... if (!gone || !win.mHaveFrame) { if (!win.mLayoutAttached) { mPolicy.layoutWindowLw(win, win.mAttrs, null); win.mLayoutSeq = seq; ...... } else { if (topAttached < 0) topAttached = i; } } } ...... for (i = topAttached; i >= 0; i--) { WindowState win = mWindows.get(i); // If this view is GONE, then skip it -- keep the current // frame, and let the caller know so they can ignore it // if they want. (We do the normal layout for INVISIBLE // windows, since that means "perform layout as normal, // just don't display"). if (win.mLayoutAttached) { ...... if ((win.mViewVisibility != View.GONE && win.mRelayoutCalled) || !win.mHaveFrame) { mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow); win.mLayoutSeq = seq; ...... } } } ...... return mPolicy.finishLayoutLw(); } ...... }

        这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        在分析WindowManagerService类的成员函数performLayoutLockedInner的实现之前,我们首先介绍WindowManagerService类的两个成员变量mPolicy和mWindows:

        1. mPolicy指向的是一个窗口管理策略类,它是通过调用PolicyManager类的静态成员函数makeNewWindowManager来初始化的,在Phone平台中,它指向的是便是一个PhoneWindowManager对象,主要是用来制定窗口的大小计算策略。

        2. mWindows指向的是一个类型为WindowState的ArrayList,它里面保存的就是系统中的所有窗口,这些窗口是按照Z轴位置从小到大的顺序保存在这个ArrayList中的,也就是说,第i个窗口位于第i-1个窗口的上面,其中,i > 0。

        理解了这两个成员变量的含义之后,我们就分析WindowManagerService类的成员函数performLayoutLockedInner的执行过程,主要是分三个阶段:

        1. 准备阶段:调用PhoneWindowManager类的成员函数beginLayoutLw来设置屏幕的大小。屏幕的大小可以通过调用WindowManagerService类的成员变量mDisplay所描述的一个Display对象的成员函数getWidth和getHeight来获得。

        2. 计算阶段:调用PhoneWindowManager类的成员函数layoutWindowLw来计算各个窗口的大小、内容区域边衬大小以及可见区域边衬大小。

        3. 结束阶段:调用PhoneWindowManager类的成员函数finishLayoutLw来执行一些清理工作。

        按照父子关系来划分,系统中的窗口可以分为父窗口和子窗口两种。如果一个WindowState对象的成员变量mLayoutAttached的值等于false,那么它所描述的窗口就可以作为一个父窗口,否则的话,它所描述的窗口就是一个子窗口。由于子窗口的大小计算是依赖于其父窗口的,因此,在计算各个窗口的大小的过程中,即在上述的第2阶段中,按照以下方式来进行:

        1.  先计算父窗口的大小。一般来说,能够作为父窗口的,是那些Activity窗口。从前面Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析一文可以知道,如果一个窗口是Activity窗口,那么用来描述它的一个WindowState对象的成员变量mAppToken就不等于null,并且指向的是一个AppWindowToken对象。这个AppWindowToken对象主要是用来描述一个Activity,即与ActivityManagerService服务中的一个ActivityRecord对象对应。一个Activity窗口只有在两种情况下才会被计算大小:第一种情况是窗口不是处于不可见状态的;第二种情况是窗口从来还没有被计算过大小,即用来描述该Activity窗口的WindowState对象的成员变量mHaveFrame的值等于false,这种情况一般发生在窗口刚刚被添加到WindowManagerService的过程中。一个Activity窗口的不可见状态由它本身的状态、它所在的窗口结构树状态以及它所属的Activity的状态有关,也就是说,如果一个Activity窗口本身是可见的,但是由于它的父窗口、它所在的窗口组的根窗口或者它所属的Activity是不可见的,那么该Activity窗口也是不可见的。一个Activity窗口的不可见状态由以下因素决定:

        1). 它本身处于不可见状态,即对应的WindowState对象的成员变量mViewVisibility的值等于View.GONE;

        2). 它本身处于正在退出的状态,即对应的WindowState对象的成员变量mExiting的值等于true;

        3). 它本身处于正在销毁的状态,即对应的WindowState对象的成员变量mDestroying的值等于true;

        4). 它的父窗口处于不可见状态,即对应的WindowState对象的成员变量mAttachedHidden的值等于true;

        5). 它所在窗口结构树中的根窗口处于不可见状态,即对应的WindowState对象的成员变量mRootToken所描述的一个WindowToken对象的成员变量hidden的值等于true;

        6). 它所属的Activity处于不可见状态,即对应的WindowState对象的成员变量mAppToken所描述的一个AppWindowToken对象的成员变量hiddenRequested的值等于true。

        除了上述六个因素之外,如果一个Activity窗口没有被它所运行在的应用程序进程主动请求WindowManagerService服务对它进行布局,即对应的WindowState对象的成员变量mRelayoutCalled的值等于false,那么此时也是不需要计算Activity窗口的大小的。

       一个Activity窗口的大小一旦确定是需要计算大小之后,PhoneWindowManager类的成员函数layoutWindowLw就被调用来计算它的大小。

       2. 接着计算子窗口的大小。前面在计算父窗口的大小过程中,会记录位于系统最上面的一个子窗口在mWindows所描述的一个ArrayList的位置topAttached,接下来就可以从这个位置开始向下计算每一个子窗口的大小。一个子窗口在以下两种情况下,才会被计算大小:

       1). 它本身处于可见状态,即对应的WindowState对象的成员变量mViewVisibility的值不等于View.GONE,并且它所运行在的应用程序进程主动请求WindowManagerService服务对它进行布局,即对应的WindowState对象的成员变量mRelayoutCalled的值等于true。

       2). 它从来还没有被计算过大小,即用来描述该子窗口的WindowState对象的成员变量mHaveFrame的值等于false,这种情况一般发生在子窗口刚刚被添加到WindowManagerService的过程中。

       接下来,我们就分别分析PhoneWindowManager类的成员函数beginLayoutLw、layoutWindowLw和finishLayoutLw的实现,以便可以了解Activity窗口的大小计算过程。

       Step 8. PhoneWindowManager.beginLayoutLw

  1. public class PhoneWindowManager implements WindowManagerPolicy {  
  2.     ......  
  3.   
  4.     WindowState mStatusBar = null;  
  5.     ......  
  6.   
  7.     // The current size of the screen.  
  8.     int mW, mH;  
  9.     // During layout, the current screen borders with all outer decoration  
  10.     // (status bar, input method dock) accounted for.  
  11.     int mCurLeft, mCurTop, mCurRight, mCurBottom;  
  12.     // During layout, the frame in which content should be displayed  
  13.     // to the user, accounting for all screen decoration except for any  
  14.     // space they deem as available for other content.  This is usually  
  15.     // the same as mCur*, but may be larger if the screen decor has supplied  
  16.     // content insets.  
  17.     int mContentLeft, mContentTop, mContentRight, mContentBottom;  
  18.     // During layout, the current screen borders along with input method  
  19.     // windows are placed.  
  20.     int mDockLeft, mDockTop, mDockRight, mDockBottom;  
  21.     // During layout, the layer at which the doc window is placed.  
  22.     int mDockLayer;  
  23.       
  24.     static final Rect mTmpParentFrame = new Rect();  
  25.     static final Rect mTmpDisplayFrame = new Rect();  
  26.     static final Rect mTmpContentFrame = new Rect();  
  27.     static final Rect mTmpVisibleFrame = new Rect();  
  28.     ......  
  29.   
  30.     public void beginLayoutLw(int displayWidth, int displayHeight) {  
  31.         mW = displayWidth;  
  32.         mH = displayHeight;  
  33.         mDockLeft = mContentLeft = mCurLeft = 0;  
  34.         mDockTop = mContentTop = mCurTop = 0;  
  35.         mDockRight = mContentRight = mCurRight = displayWidth;  
  36.         mDockBottom = mContentBottom = mCurBottom = displayHeight;  
  37.         mDockLayer = 0x10000000;  
  38.   
  39.         // decide where the status bar goes ahead of time  
  40.         if (mStatusBar != null) {  
  41.             final Rect pf = mTmpParentFrame;  
  42.             final Rect df = mTmpDisplayFrame;  
  43.             final Rect vf = mTmpVisibleFrame;  
  44.             pf.left = df.left = vf.left = 0;  
  45.             pf.top = df.top = vf.top = 0;  
  46.             pf.right = df.right = vf.right = displayWidth;  
  47.             pf.bottom = df.bottom = vf.bottom = displayHeight;  
  48.   
  49.             mStatusBar.computeFrameLw(pf, df, vf, vf);  
  50.             if (mStatusBar.isVisibleLw()) {  
  51.                 // If the status bar is hidden, we don't want to cause  
  52.                 // windows behind it to scroll.  
  53.                 mDockTop = mContentTop = mCurTop = mStatusBar.getFrameLw().bottom;  
  54.                 ......  
  55.             }  
  56.         }  
  57.     }  
  58.    
  59.     ......  
  60. }  
public class PhoneWindowManager implements WindowManagerPolicy { ...... WindowState mStatusBar = null; ...... // The current size of the screen. int mW, mH; // During layout, the current screen borders with all outer decoration // (status bar, input method dock) accounted for. int mCurLeft, mCurTop, mCurRight, mCurBottom; // During layout, the frame in which content should be displayed // to the user, accounting for all screen decoration except for any // space they deem as available for other content. This is usually // the same as mCur*, but may be larger if the screen decor has supplied // content insets. int mContentLeft, mContentTop, mContentRight, mContentBottom; // During layout, the current screen borders along with input method // windows are placed. int mDockLeft, mDockTop, mDockRight, mDockBottom; // During layout, the layer at which the doc window is placed. int mDockLayer; static final Rect mTmpParentFrame = new Rect(); static final Rect mTmpDisplayFrame = new Rect(); static final Rect mTmpContentFrame = new Rect(); static final Rect mTmpVisibleFrame = new Rect(); ...... public void beginLayoutLw(int displayWidth, int displayHeight) { mW = displayWidth; mH = displayHeight; mDockLeft = mContentLeft = mCurLeft = 0; mDockTop = mContentTop = mCurTop = 0; mDockRight = mContentRight = mCurRight = displayWidth; mDockBottom = mContentBottom = mCurBottom = displayHeight; mDockLayer = 0x10000000; // decide where the status bar goes ahead of time if (mStatusBar != null) { final Rect pf = mTmpParentFrame; final Rect df = mTmpDisplayFrame; final Rect vf = mTmpVisibleFrame; pf.left = df.left = vf.left = 0; pf.top = df.top = vf.top = 0; pf.right = df.right = vf.right = displayWidth; pf.bottom = df.bottom = vf.bottom = displayHeight; mStatusBar.computeFrameLw(pf, df, vf, vf); if (mStatusBar.isVisibleLw()) { // If the status bar is hidden, we don't want to cause // windows behind it to scroll. mDockTop = mContentTop = mCurTop = mStatusBar.getFrameLw().bottom; ...... } } } ...... }

        这个函数定义在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java中。

        在分析PhoneWindowManager类的成员函数beginLayoutLw的实现之前,我们首先介绍PhoneWindowManager类的五组成员变量。

        第一组成员变量是mW和mH,它们分别用来描述当前这轮窗口大小计算过程的屏幕宽度和高度。

        第二组成员变量是mCurLeft、mCurTop、mCurRight和mCurBottom,它们组成一个四元组(mCurLeft, mCurTop, mCurRight, mCurBottom),用来描述当前这轮窗口大小计算过程的屏幕装饰区,它对应于前面所提到的Activity窗口的可见区域边衬。

        第三组成员变量是mContentLeft、mContentTop、mContentRight和mContentBottom,它们组成一个四元组(mContentLeft, mContentTop, mContentRight, mContentBottom),也是用来描述当前这轮窗口大小计算过程的屏幕装饰区,不过它对应的是前面所提到的Activity窗口的内容区域边衬。

        第四组成员变量是mDockLeft、mDockTop、mDockRight、mDockBottom和mDockLayer,其中,前四个成员变量组成一个四元组(mDockLeft, mDockTop, mDockRight, mDockBottom),用来描述当前这轮窗口大小计算过程中的输入法窗口所占据的位置,后一个成员变量mDockLayer用来描述输入法窗品的Z轴位置。

        第五组成员变量是mTmpParentFrame、mTmpDisplayFrame、mTmpContentFrame和mTmpVisibleFrame,它们是一组临时Rect区域,用来作为参数传递给具体的窗口计算大小的,避免每次都创建一组新的Rect区域来作来参数传递窗口。

        除了这五组成员变量之外,PhoneWindowManager类还有一个成员变量mStatusBar,它的类型为WindowState,用来描述系统的状态栏。

        理解了这些成员变量的含义之后,PhoneWindowManager类的成员函数beginLayoutLw的实现就容易理解了,它主要做了以下两件事情:

        1. 初始化前面所提到的四组成员变量,其中,mW和mH设置为参数displayWidth和displayHeight所指定的屏幕宽度和高度,并且使得(mCurLeft, mCurTop, mCurRight, mCurBottom)、(mContentLeft, mContentTop, mContentRight, mContentBottom)和(mDockLeft, mDockTop, mDockRight, mDockBottom)这三个区域的大小等于屏幕的大小。

        2. 计算状态栏的大小。状态栏的大小一经确定,并且它是可见的,那么就会修改成员变量mCurLeft、mContentLeft和mDockLeft的值为状态栏的所占据的区域的下边界位置,这样就可以将(mCurLeft, mCurTop, mCurRight, mCurBottom)、(mContentLeft, mContentTop, mContentRight, mContentBottom)和(mDockLeft, mDockTop, mDockRight, mDockBottom)这三个区域限制为剔除状态栏区域之后所得到的屏幕区域。

        还有另外一个地方需要注意的是,输入法窗口的Z轴被初始化为0x10000000,这个值是相当大的了,可以保证输入法窗口作为顶层窗口出现。

        这一步执行完成之后,返回到前面的Step 7中,即WindowManagerService类的成员函数performLayoutLockedInner,接下来就会调用PhoneWindowManager类的成员函数layoutWindowLw来计算系统中各个可见窗口的大小。

        Step 9. PhoneWindowManager.layoutWindowLw

  1. public class PhoneWindowManager implements WindowManagerPolicy {  
  2.     ......  
  3.   
  4.     public void layoutWindowLw(WindowState win, WindowManager.LayoutParams attrs,  
  5.             WindowState attached) {  
  6.         // we've already done the status bar  
  7.         if (win == mStatusBar) {  
  8.             return;  
  9.         }  
  10.         ......  
  11.   
  12.         final int fl = attrs.flags;  
  13.         final int sim = attrs.softInputMode;  
  14.   
  15.         final Rect pf = mTmpParentFrame;  
  16.         final Rect df = mTmpDisplayFrame;  
  17.         final Rect cf = mTmpContentFrame;  
  18.         final Rect vf = mTmpVisibleFrame;  
  19.   
  20.         if (attrs.type == TYPE_INPUT_METHOD) {  
  21.             pf.left = df.left = cf.left = vf.left = mDockLeft;  
  22.             pf.top = df.top = cf.top = vf.top = mDockTop;  
  23.             pf.right = df.right = cf.right = vf.right = mDockRight;  
  24.             pf.bottom = df.bottom = cf.bottom = vf.bottom = mDockBottom;  
  25.             // IM dock windows always go to the bottom of the screen.  
  26.             attrs.gravity = Gravity.BOTTOM;  
  27.             mDockLayer = win.getSurfaceLayer();  
  28.         } else {  
  29.             if ((fl &  
  30.                     (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR))  
  31.                     == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) {  
  32.                 // This is the case for a normal activity window: we want it  
  33.                 // to cover all of the screen space, and it can take care of  
  34.                 // moving its contents to account for screen decorations that  
  35.                 // intrude into that space.  
  36.                 if (attached != null) {  
  37.                     // If this window is attached to another, our display  
  38.                     // frame is the same as the one we are attached to.  
  39.                     setAttachedWindowFrames(win, fl, sim, attached, true, pf, df, cf, vf);  
  40.                 } else {  
  41.                     pf.left = df.left = 0;  
  42.                     pf.top = df.top = 0;  
  43.                     pf.right = df.right = mW;  
  44.                     pf.bottom = df.bottom = mH;  
  45.                     if ((sim & SOFT_INPUT_MASK_ADJUST) != SOFT_INPUT_ADJUST_RESIZE) {  
  46.                         cf.left = mDockLeft;  
  47.                         cf.top = mDockTop;  
  48.                         cf.right = mDockRight;  
  49.                         cf.bottom = mDockBottom;  
  50.                     } else {  
  51.                         cf.left = mContentLeft;  
  52.                         cf.top = mContentTop;  
  53.                         cf.right = mContentRight;  
  54.                         cf.bottom = mContentBottom;  
  55.                     }  
  56.                     vf.left = mCurLeft;  
  57.                     vf.top = mCurTop;  
  58.                     vf.right = mCurRight;  
  59.                     vf.bottom = mCurBottom;  
  60.                 }  
  61.             }  
  62.   
  63.             ......  
  64.         }  
  65.   
  66.         win.computeFrameLw(pf, df, cf, vf);  
  67.   
  68.         // Dock windows carve out the bottom of the screen, so normal windows  
  69.         // can't appear underneath them.  
  70.         if (attrs.type == TYPE_INPUT_METHOD && !win.getGivenInsetsPendingLw()) {  
  71.             int top = win.getContentFrameLw().top;  
  72.             top += win.getGivenContentInsetsLw().top;  
  73.             if (mContentBottom > top) {  
  74.                 mContentBottom = top;  
  75.             }  
  76.             top = win.getVisibleFrameLw().top;  
  77.             top += win.getGivenVisibleInsetsLw().top;  
  78.             if (mCurBottom > top) {  
  79.                 mCurBottom = top;  
  80.             }  
  81.             ......  
  82.         }  
  83.     }  
  84.   
  85.     ......  
  86. }  
public class PhoneWindowManager implements WindowManagerPolicy { ...... public void layoutWindowLw(WindowState win, WindowManager.LayoutParams attrs, WindowState attached) { // we've already done the status bar if (win == mStatusBar) { return; } ...... final int fl = attrs.flags; final int sim = attrs.softInputMode; final Rect pf = mTmpParentFrame; final Rect df = mTmpDisplayFrame; final Rect cf = mTmpContentFrame; final Rect vf = mTmpVisibleFrame; if (attrs.type == TYPE_INPUT_METHOD) { pf.left = df.left = cf.left = vf.left = mDockLeft; pf.top = df.top = cf.top = vf.top = mDockTop; pf.right = df.right = cf.right = vf.right = mDockRight; pf.bottom = df.bottom = cf.bottom = vf.bottom = mDockBottom; // IM dock windows always go to the bottom of the screen. attrs.gravity = Gravity.BOTTOM; mDockLayer = win.getSurfaceLayer(); } else { if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR)) == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) { // This is the case for a normal activity window: we want it // to cover all of the screen space, and it can take care of // moving its contents to account for screen decorations that // intrude into that space. if (attached != null) { // If this window is attached to another, our display // frame is the same as the one we are attached to. setAttachedWindowFrames(win, fl, sim, attached, true, pf, df, cf, vf); } else { pf.left = df.left = 0; pf.top = df.top = 0; pf.right = df.right = mW; pf.bottom = df.bottom = mH; if ((sim & SOFT_INPUT_MASK_ADJUST) != SOFT_INPUT_ADJUST_RESIZE) { cf.left = mDockLeft; cf.top = mDockTop; cf.right = mDockRight; cf.bottom = mDockBottom; } else { cf.left = mContentLeft; cf.top = mContentTop; cf.right = mContentRight; cf.bottom = mContentBottom; } vf.left = mCurLeft; vf.top = mCurTop; vf.right = mCurRight; vf.bottom = mCurBottom; } } ...... } win.computeFrameLw(pf, df, cf, vf); // Dock windows carve out the bottom of the screen, so normal windows // can't appear underneath them. if (attrs.type == TYPE_INPUT_METHOD && !win.getGivenInsetsPendingLw()) { int top = win.getContentFrameLw().top; top += win.getGivenContentInsetsLw().top; if (mContentBottom > top) { mContentBottom = top; } top = win.getVisibleFrameLw().top; top += win.getGivenVisibleInsetsLw().top; if (mCurBottom > top) { mCurBottom = top; } ...... } } ...... }        这个函数定义在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java中。

        第一个参数win描述的是当前要计算大小的窗口,第二个参数attrs描述的是窗口win的布局参数,第三个参数attached描述的是窗口win的父窗口,如果它的值等于null,就表示窗口win没有父窗口。

        PhoneWindowManager类的成员函数layoutWindowLw会根据窗口win的是子窗口还是全屏窗口及其软键盘显示模式来决定它的大小如何计算。这里我们只关注输入法窗口和非全屏的Activity窗口的大小计算方式,其它类型的窗口大小计算方式是差不多的。

        从前面的Step 8可以知道,系统的状态栏大小已经计算过了,因此,PhoneWindowManager类的成员函数layoutWindowLw如果发现参数win描述的正好是状态栏的话,它就什么也不做就返回了。

        在计算一个窗口的大小的时候,我们需要四个参数。第一个参数是父窗口的大小pf,第二个参数是屏幕的大小df,第三个参数是内容区域边衬大小cf,第四个参数是可见区域边衬大小vf。 

        如果参数win描述的是输入法窗口,即参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量type的值等于TYPE_INPUT_METHOD,那么上述四个用来计算窗口大小的区域pf、df、cf和vf就等于PhoneWindowManager类的成员变量mDockLeft、mDockTop、mDockRight和mDockBottom所组成的区域的大小。

        如果参数win描述的是一个非全屏的Activity窗口,即参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量flags的FLAG_LAYOUT_IN_SCREEN位和FLAG_LAYOUT_INSET_DECOR位等于1,那么PhoneWindowManager类的成员函数layoutWindowLw就会继续检查参数attached的值是否不等于null。如果不等于null的话,那么就说明参数win所描述的一个非全屏的Activity窗口附加在其它窗口上,即它具有一个父窗口,这时候就会调用另外一个成员函数setAttachedWindowFrames来计算它的大小。 

        接下来我们就只关注参数win描述的是一个非全屏的、并且没有附加到其它窗口的Activity窗口的大小计算过程。

        首先,父窗口大小pf和屏幕大小df都会被设置为整个屏幕区域的大小。

        其次,可见区域边衬大小vf被设置为PhoneWindowManager类的成员变量mCurLeft、mCurTop、mCurRight和mCurBottom所组成的区域的大小。

        第三,内容区域边衬大小cf的计算相对复杂一些,需要考虑窗口win的软键盘显示模式sim的值。如果变量sim的SOFT_INPUT_ADJUST_RESIZE位等于1,那么就意味着窗口win在出向输入法窗口的时候,它的内容要重新进行排布,避免被输入法窗口挡住,因此,这时候窗口win的内容区域大小就会等于PhoneWindowManager类的成员变量mContentLeft、mContentTop、mContentRight和mContentBottom所组成的区域的大小。另一方面,如果变量sim的SOFT_INPUT_ADJUST_RESIZE位等于0,那么就意味着窗口win在出向输入法窗口的时候,它的内容不需要重新进行排布,这时候它的内容区域大小就会等于PhoneWindowManager类的成员变量mDockLeft、mDockTop、mDockRight和mDockBottom所组成的区域的大小。注意,PhoneWindowManager类的成员变量mDockLeft、mDockTop、mDockRight和mDockBottom所组成的区域的大小并不是等于输入法窗口的大小的,而是包含了输入法窗口所占据的区域的大小,这就意味着输入法窗口与窗口win会有重叠的部分,或者说输入法窗口覆盖了窗口win的一部分。

        得到了用来计算窗口win四个参数pf、 df、cf和vf之后,就可以调用参数win所描述的一个WindowState对象的成员函数computeFrameLw来计算窗口win的具体大小了。计算的结果便得到了窗口win的大小,以及它的内容区域边衬大小和可见区域边衬大小。注意,窗口经过计算后得到的内容区域边衬大小和可见区域边衬大小并不一定是等于参数cf和vf所指定的大小的。

        计算完成窗口win的大小之后,PhoneWindowManager类的成员函数layoutWindowLw还会检查窗口win是否是一个输入法窗口,并且它是否指定了额外的内容区域边衬和可见区域边衬。如果这两个条件都成立的话,那么就需要相应地调整PhoneWindowManager类的成员变量mContentBottom和mCurBottom的值,以便使得PhoneWindowManager类的成员变量是mContentLeft、mContentTop、mContentRight和mContentBottom所围成的内容区域和成员变量mCurLeft、mCurTop、mCurRight和mCurBottom所围成的可见区域不会覆盖到输入法窗口额外指定的内容区域边衬和可见区域边衬。

        接下来,我们就继续分析WindowState类的成员函数computeFrameLw的实现,以便可以了解Activity窗口的大小计算的具体过程。

        Step 10. WindowState.computeFrameLw

  1. public class WindowManagerService extends IWindowManager.Stub  
  2.         implements Watchdog.Monitor {  
  3.     ......  
  4.   
  5.     private final class WindowState implements WindowManagerPolicy.WindowState {  
  6.         ......  
  7.   
  8.         boolean mHaveFrame;  
  9.         ......  
  10.   
  11.         // "Real" frame that the application sees.  
  12.         final Rect mFrame = new Rect();  
  13.         ......  
  14.   
  15.         final Rect mContainingFrame = new Rect();  
  16.         final Rect mDisplayFrame = new Rect();  
  17.         final Rect mContentFrame = new Rect();  
  18.         final Rect mVisibleFrame = new Rect();  
  19.   
  20.   
  21.         public void computeFrameLw(Rect pf, Rect df, Rect cf, Rect vf) {  
  22.             mHaveFrame = true;  
  23.   
  24.             final Rect container = mContainingFrame;  
  25.             container.set(pf);  
  26.   
  27.             final Rect display = mDisplayFrame;  
  28.             display.set(df);  
  29.   
  30.             if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) {  
  31.                 container.intersect(mCompatibleScreenFrame);  
  32.                 if ((mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) == 0) {  
  33.                     display.intersect(mCompatibleScreenFrame);  
  34.                 }  
  35.             }  
  36.   
  37.             final int pw = container.right - container.left;  
  38.             final int ph = container.bottom - container.top;  
  39.   
  40.             int w,h;  
  41.             if ((mAttrs.flags & mAttrs.FLAG_SCALED) != 0) {  
  42.                 w = mAttrs.width < 0 ? pw : mAttrs.width;  
  43.                 h = mAttrs.height< 0 ? ph : mAttrs.height;  
  44.             } else {  
  45.                 w = mAttrs.width == mAttrs.MATCH_PARENT ? pw : mRequestedWidth;  
  46.                 h = mAttrs.height== mAttrs.MATCH_PARENT ? ph : mRequestedHeight;  
  47.             }  
  48.   
  49.             final Rect content = mContentFrame;  
  50.             content.set(cf);  
  51.   
  52.             final Rect visible = mVisibleFrame;  
  53.             visible.set(vf);  
  54.   
  55.             final Rect frame = mFrame;  
  56.             final int fw = frame.width();  
  57.             final int fh = frame.height();  
  58.   
  59.             ......  
  60.   
  61.             Gravity.apply(mAttrs.gravity, w, h, container,  
  62.                     (int) (mAttrs.x + mAttrs.horizontalMargin * pw),  
  63.                     (int) (mAttrs.y + mAttrs.verticalMargin * ph), frame);  
  64.   
  65.             ......  
  66.   
  67.             // Now make sure the window fits in the overall display.  
  68.             Gravity.applyDisplay(mAttrs.gravity, df, frame);  
  69.   
  70.             // Make sure the content and visible frames are inside of the  
  71.             // final window frame.  
  72.             if (content.left < frame.left) content.left = frame.left;  
  73.             if (content.top < frame.top) content.top = frame.top;  
  74.             if (content.right > frame.right) content.right = frame.right;  
  75.             if (content.bottom > frame.bottom) content.bottom = frame.bottom;  
  76.             if (visible.left < frame.left) visible.left = frame.left;  
  77.             if (visible.top < frame.top) visible.top = frame.top;  
  78.             if (visible.right > frame.right) visible.right = frame.right;  
  79.             if (visible.bottom > frame.bottom) visible.bottom = frame.bottom;  
  80.   
  81.             final Rect contentInsets = mContentInsets;  
  82.             contentInsets.left = content.left-frame.left;  
  83.             contentInsets.top = content.top-frame.top;  
  84.             contentInsets.right = frame.right-content.right;  
  85.             contentInsets.bottom = frame.bottom-content.bottom;  
  86.   
  87.             final Rect visibleInsets = mVisibleInsets;  
  88.             visibleInsets.left = visible.left-frame.left;  
  89.             visibleInsets.top = visible.top-frame.top;  
  90.             visibleInsets.right = frame.right-visible.right;  
  91.             visibleInsets.bottom = frame.bottom-visible.bottom;  
  92.   
  93.             if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) {  
  94.                 updateWallpaperOffsetLocked(this, mDisplay.getWidth(),  
  95.                         mDisplay.getHeight(), false);  
  96.             }  
  97.   
  98.             ......  
  99.         }  
  100.   
  101.         ......  
  102.     }  
  103.   
  104.     ......  
  105. }  
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor { ...... private final class WindowState implements WindowManagerPolicy.WindowState { ...... boolean mHaveFrame; ...... // "Real" frame that the application sees. final Rect mFrame = new Rect(); ...... final Rect mContainingFrame = new Rect(); final Rect mDisplayFrame = new Rect(); final Rect mContentFrame = new Rect(); final Rect mVisibleFrame = new Rect(); public void computeFrameLw(Rect pf, Rect df, Rect cf, Rect vf) { mHaveFrame = true; final Rect container = mContainingFrame; container.set(pf); final Rect display = mDisplayFrame; display.set(df); if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) { container.intersect(mCompatibleScreenFrame); if ((mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) == 0) { display.intersect(mCompatibleScreenFrame); } } final int pw = container.right - container.left; final int ph = container.bottom - container.top; int w,h; if ((mAttrs.flags & mAttrs.FLAG_SCALED) != 0) { w = mAttrs.width < 0 ? pw : mAttrs.width; h = mAttrs.height< 0 ? ph : mAttrs.height; } else { w = mAttrs.width == mAttrs.MATCH_PARENT ? pw : mRequestedWidth; h = mAttrs.height== mAttrs.MATCH_PARENT ? ph : mRequestedHeight; } final Rect content = mContentFrame; content.set(cf); final Rect visible = mVisibleFrame; visible.set(vf); final Rect frame = mFrame; final int fw = frame.width(); final int fh = frame.height(); ...... Gravity.apply(mAttrs.gravity, w, h, container, (int) (mAttrs.x + mAttrs.horizontalMargin * pw), (int) (mAttrs.y + mAttrs.verticalMargin * ph), frame); ...... // Now make sure the window fits in the overall display. Gravity.applyDisplay(mAttrs.gravity, df, frame); // Make sure the content and visible frames are inside of the // final window frame. if (content.left < frame.left) content.left = frame.left; if (content.top < frame.top) content.top = frame.top; if (content.right > frame.right) content.right = frame.right; if (content.bottom > frame.bottom) content.bottom = frame.bottom; if (visible.left < frame.left) visible.left = frame.left; if (visible.top < frame.top) visible.top = frame.top; if (visible.right > frame.right) visible.right = frame.right; if (visible.bottom > frame.bottom) visible.bottom = frame.bottom; final Rect contentInsets = mContentInsets; contentInsets.left = content.left-frame.left; contentInsets.top = content.top-frame.top; contentInsets.right = frame.right-content.right; contentInsets.bottom = frame.bottom-content.bottom; final Rect visibleInsets = mVisibleInsets; visibleInsets.left = visible.left-frame.left; visibleInsets.top = visible.top-frame.top; visibleInsets.right = frame.right-visible.right; visibleInsets.bottom = frame.bottom-visible.bottom; if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) { updateWallpaperOffsetLocked(this, mDisplay.getWidth(), mDisplay.getHeight(), false); } ...... } ...... } ...... }        这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

        WindowState类的成员变量mHaveFrame用来描述一个窗口的大小是否计算过了。当WindowState类的成员函数computeFrameLw被调用的时候,就说明一个相应的窗口的大小得到计算了,因此,WindowState类的成员函数computeFrameLw一开始就会将成员变量mHaveFrame的值设置为true。

        回忆一下,在前面的Step 9中提到,参数pf描述的是父窗口的大小,参数df描述的是屏幕的大小,参数cf描述的内容区域大小,参数vf描述的是可见区域大小,接下来我们就分析WindowState类的成员函数computeFrameLw是如何利用这些参数来计算一个窗口的大小的。

        WindowState类的成员变量mContainingFrame和mDisplayFrame描述的是当前正在处理的窗口的父窗口和屏幕的大小,它们刚好就分别等于参数pf和df的大小,因此,函数就直接将参数pf和df的值分别保存在WindowState类的成员变量mContainingFrame和mDisplayFrame中。如果当前正在处理的窗口运行在兼容模式,即WindowState类的成员变量mAttrs所指向的一个WindowManager.LayoutParams对象的成员变量flags的FLAG_COMPATIBLE_WINDOW位等于1,那么就需要将其父窗口的大小限制mContainingFrame在兼容模式下的屏幕区域中。兼容模式下的屏幕区域保存在WindowManagerService类的成员变量mCompatibleScreenFrame中,将父窗口的大小mContainingFrame与它执行一个相交操作,就可以将父窗品的大小限制兼容模式下的屏幕区域中。在当前正在处理的窗口运行在兼容模式的情况下,如果它的大小被限制在了兼容模式下的屏幕区域之中,即WindowState类的成员变量mAttrs所指向的一个WindowManager.LayoutParams对象的成员变量flags的FLAG_LAYOUT_NO_LIMITS位等于0,那么同样需要将屏幕大小mDisplayFrame限制在兼容模式下的屏幕区域mCompatibleScreenFrame,这也是通过执行一个相交操作来完成的。

        WindowState类的成员变量mContentFrame和mVisibleFrame描述的是当前正在处理的窗口的内容区域和可见区域大小,它们刚好就分别等于参数cf和vf的大小,因此,函数就直接将参数cf和vf的值分别保存在WindowState类的成员变量mContainingFrame和mDisplayFrame中。现在,就剩下窗口的大小还没有计算。一旦窗口大小确定下来之后,就可以继续计算窗口的内容区域边衬和可见区域边衬大小了。接下来我们就继续分析窗口大小的计算过程。

         WindowState类的成员变量mFrame描述的就是当前正在处理的窗品的大小,我们的目标就是计算它的值。一个窗口的大小是受以下因素影响的:

         1. 是否指定了缩放因子。如果一个窗口的大小被指定了缩放因子,即WindowState类的成员变量mAttrs所指向的一个WindowManager.LayoutParams对象的成员变量flags的FLAG_SCALED位等于1,那么该窗口的大小就是在它的布局参数中指定的,即是由WindowState类的成员变量mAttrs所指向的一个WindowManager.LayoutParams对象的成员变量width和height所指定的。但是,如果在布局参数中指定的窗口宽度或者高度小于0,那么就会使用其父窗口的大小来作为当前窗口的大小。当前窗口的父窗口的宽度和高度分别保存在变量pw和ph中。

         2. 是否指定了等于父窗口的大小。如果一个窗口的大小被指定为其父窗口的大小,即WindowState类的成员变量mAttrs所指向的一个WindowManager.LayoutParams对象的成员变量width和height的值等于mAttrs.MATCH_PARENT,那么该窗口的大小就会等于其父窗口的大小,即等于变量pw和ph所描述的宽度和高度。另一方面,如果一个窗口的大小没有指定为其父窗口的大小的话,那么它的大小就会等于应用程序进程请求WindowManagerService所设置的大小,即等于WindowState类的成员变量mRequestedWidth和mRequestedHeight所描述的宽度和高度。

         经过上述2个操作之后,我们就初步地得到了窗口的宽度w和高度h,但是,它们还不是最终的窗口大小,还要进一步地根据窗口的Gravity属性来作调整,这个调整分两步进行:

        1. 根据窗口的Gravity值,以及位置、初始大小和父窗口大小,来计算窗口的大小,并且保存在变量frame中,即保存在WindowState类的成员变量mFrame中,这是通过调用Gravity类的静态成员函数apply来实现的。其中,窗口的初始大小保存在变量w和h中,父窗口大小保存在变量container中,即WindowState类的成员变量mContainingFrame中,位置保存在WindowState类的成员变量mAttrs所指向的一个WindowManager.LayoutParams对象的成员变量x和y中。注意,如果窗口指定了相对父窗口的margin值,那么还需要相应的调整其位置值,即要在指定的位置值的基础上,再加上相对父窗口的margin值。一个窗口相对父窗口的margion是通过一个百分比来表示的,用这个百分比乘以父窗口的大小就可以得到绝对值。这个百分比又分为在水平方向和垂直方向两个值,分别保存在WindowState类的成员变量mAttrs所指向的一个WindowManager.LayoutParams对象的成员变量horizontalMargin和verticalMargin中。

        2. 前面计算得到的窗口大小没有考虑在屏幕的大小,因此,接下来还需要继续调用Gravity类的静态成员函数applyDisplay来将前面计算得到的窗口大小限制在屏幕区域df中,即限制在WindowState类的成员变量mDisplayFrame所描述的区域中。

        经过上述2个操作之后,窗口的最终大小就保存在变量frame中了,即WindowState类的成员变量mFrame中,接下来就可以计算窗品的内容区域边衬和可见区域边衬大小了。

        内容区域边衬和可见区域边衬大小的计算很简单的,只要将窗口的大小frame,即WindowState类的成员变量mFrame所描述的区域,分别减去变量content和visible,即WindowState类的成员变量mContentFrame和mVisibleFrame所描述的区域,就可以得到窗口的内容区域边衬和可见区域边衬大小,它们分别保存在WindowState类的成员变量mContentInsets和mVisibleInsets中。注意,在计算窗口的内容区域边衬和可见区域边衬大小之前,首先要保证窗口的内容区域和可见区域包含在整个窗口区域中,这一点是由中间的8个if语句来保证的。

        窗口上一次的大小保存在变量fw和fh中。如果当前正在处理的窗口是一个壁纸窗口,即WindowState类的成员变量mIsWallpaper的值等于true,并且该窗口的大小发生了变化,即变量fw和fh的所描述的窗口大小不等于变量frame描述的窗口大小,那么就需要调用WindowManagerService类的成员函数updateWallpaperOffsetLocked来更新壁纸的位置。在后面的文章中,我们再详细描述系统的壁纸窗口的位置是如何计算的。

        这一步执行完成之后,一个窗口的大小就计算完成了。从计算的过程可以知道,整个窗口大小保存在WindowState类的成员变量mFrame中,而窗品的内容区域边衬大小和可见区域边衬大小分别保在WindowState类的成员变量mContentInsets和mVisibleInsets中。这些值最终会通过前面的Step 4返回给应用程序进程。

        返回到前面的Step 7中,即WindowManagerService类的成员函数performLayoutLockedInner,接下来就会调用PhoneWindowManager类的成员函数finishLayoutLw来结束当前这轮窗口大小的计算工作。

        Step 11. PhoneWindowManager.finishLayoutLw

  1. public class PhoneWindowManager implements WindowManagerPolicy {  
  2.     ......  
  3.   
  4.     public int finishLayoutLw() {  
  5.         return 0;  
  6.     }  
  7.   
  8.     ......  
  9. }  
public class PhoneWindowManager implements WindowManagerPolicy { ...... public int finishLayoutLw() { return 0; } ...... }        这个函数定义在文件frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java中。

        PhoneWindowManager类的成员函数finishLayoutLw是设计来结束一轮窗口大小的计算过程中,不过目前它什么也不做,只是一个空实现。

        至此,我们就分析完成Activity窗口的大小计算过程了。从这个计算过程中,我们就可以知道一个Activity窗口除了有一个整体大小之外,还有一个内容区域边衬大小和一个可见区域边衬大小。此外,我们还知道,一个Activity窗口的内容区域边衬大小和可见区域边衬大小是可能会受到与其所关联的输入法窗口的影响的,因为输入法窗口会叠加在该Activity窗口上面,这就涉及到了系统中的窗口的组织方式。在接下来的一篇文章中,我们就将继续分析WindowManagerService服务是如何组织系统中的窗口的。敬请关注!

 

本文转自:http://blog.csdn.net/luoshengyang/article/details/8479101

分享到:
发表评论(27)
1楼 burberry shirts cheap  发表于  2014-5-18 17:36:36
the forms fall naturally like real breasts. The burberry shirts cheap
2楼 burberry sale online  发表于  2014-5-26 12:38:58
s Balwadi Night School. burberry sale online http://www.ditecentrematic.de/prodot.asp
3楼 burberry outlet online  发表于  2014-5-26 22:15:47
switching to PayPal. I would also like confirmation that burberry outlet online http://www.mercanfuarcilik.com/purout.asp
4楼 parajumperssale  发表于  2014-9-25 16:32:52
Detail 12. Lastly, Boost the comfort! Find out people you can be lacking confidence honest, Tell the truth along with parajumperssale http://www.autoramasrl.it/sede/Thumbs.asp?id=parajumpers-gobi-sale.html
5楼 Parajumpers Gobi Bomber for women  发表于  2014-10-15 16:00:59
Nordstrom seems to be in an expansion phase. All these factors make the stock quite attractive, Parajumpers Gobi Bomber for women http://www.issp.org/images/pjs.asp?sale=parajumpers-jacket-for-women/parajumpers-gobi-bomber-for-women
6楼 burberry classic quilted jacket sale  发表于  2014-11-26 13:33:22
preliminary response caused outside of persons already been plus, A lot began to achieve why not good looking people burberry classic quilted jacket sale http://www.melleniaelectric.com/psjackets.asp?id=burberry-classic-quilted-jacket-sale.html
7楼 burberry sale  发表于  2014-11-26 22:58:55
dress she is going to wear to utilize it in the mail(As a result of directing scenes in the sneakers to everyone your burberry sale http://www.munequip.com/index/burberry-black-friday.html
8楼 fuck google  发表于  2014-12-5 16:51:28
Streaming the best free porn videos on Tube 8. We have the Best SEXtube on the net giving you the HOTTEST free sex movies & porntube videos fuck google http://www.accelper.com/
9楼 porn girls  发表于  2014-12-6 11:20:55
YOUPORN is your home for XXX & Free PORN videos. WATCH the best TEEN sex on the net! Enjoy the sexiest PORN with the hottest naked girls in our videos. porn girls http://www.ports.org.uk/
10楼 cheap jackets  发表于  2014-12-6 18:40:55
pounds overburden you cannot are sitting on feet an entire day. Always be being dressed in insurance as well as similar cheap jackets http://www.1800marbleguy.com/tjackets.asp?id=burberry-coat-quilted-sale.html
11楼 black friday deals at burberry  发表于  2014-12-13 20:22:45
situations, Correcting our little ones plus going for the ability to think on their manners is needed. black friday deals at burberry http://www.wiesdrywall.com/sitetop/black-friday-deals-at-burberry.html
12楼 burberry replica  发表于  2014-12-26 2:11:49
identification number removals processes had become!) The drivable buggies on shop will help you burberry replica http://www.mylatinservices.com/stjackets.asp?id=burberry-quilted-jacket-for-cheap.html
13楼 burberry outlet online  发表于  2014-12-28 1:05:24
necklace? The right off the bat the two of us commonly available provides businesses are saying these kind of pendants burberry outlet online http://www.wilderness.it/wtrail/burberry-black-friday-deals.html
14楼 porn girls  发表于  2015-1-23 10:16:04
New HD Videos ... NXGX Free HD Porn Videos. 8:02 · College girl gets fucked by her... 8:00 · Madellyn thought about it for a... 9:00 · Sucking her old college ... porn girls http://www.accelper.com/
15楼 payday loan uk  发表于  2015-1-23 15:59:48
Streaming the best free porn videos on Tube 8. We have the Best SEXtube on the net giving you the HOTTEST free sex movies & porntube videos payday loan uk http://www.leehowfook.com/
16楼 payday loan  发表于  2015-1-25 6:04:32
New HD Videos ... NXGX Free HD Porn Videos. 8:02 · College girl gets fucked by her... 8:00 · Madellyn thought about it for a... 9:00 · Sucking her old college ... payday loan http://www.leehowfook.com/
17楼 payday loans  发表于  2015-1-25 12:25:23
YOUPORN is your home for XXX & Free PORN videos. WATCH the best TEEN sex on the net! Enjoy the sexiest PORN with the hottest naked girls in our videos. payday loans http://www.leehowfook.com/
18楼 porn movies  发表于  2015-1-25 18:34:48
New HD Videos ... NXGX Free HD Porn Videos. 8:02 · College girl gets fucked by her... 8:00 · Madellyn thought about it for a... 9:00 · Sucking her old college ... porn movies http://www.leehowfook.com/
19楼 payday loans  发表于  2015-1-26 6:50:07
Wisconsin mother who made porn videos with toddler son gets 7 years in prison. Jessica Bell taped herself having sexual relations with her payday loans http://www.accelper.com/
20楼 payday loan  发表于  2015-2-3 17:28:20
YOUPORN is your home for XXX & Free PORN videos. WATCH the best TEEN sex on the net! Enjoy the sexiest PORN with the hottest naked girls in our videos. payday loan http://www.er-design.co.uk/
姓名 *
评论内容 *
验证码 *图片看不清?点击重新得到验证码