精华内容
下载资源
问答
  • 本文出自:... 最基本的C++ 窗口程序代码及完整注释 2009-05-31 18:51 //我写了一个C++ windows应用程序框架代码,供参考,此源代码属函数编程,涉及MFC类库,可以保证程序的灵活性,//时




    最基本的C++ 窗口程序代码及完整注释
    2009-05-31 18:51

    //我写了一个C++ windows应用程序框架代码,供参考,此源代码属函数编程,不涉及MFC类库,可以保证程序的灵活性,
    //时间有限,部分代码的注释可能不太精确,毕尽windows编程太过精深,不是一两句话可以说清楚的,最好在编译器上使用,
    //这样比较容易看明白,已经使用VC 6.0 验证。

    //基本注释
    // 纷雪繁沙   2009.05.31 c++   visual c++ 6.0   windows xp   windows.cpp

    //编写预处理命/
    #include <windows.h>   // windows头文件

    //声明全局变量/
    //这只是一个框架程序所以还没有全局变量

    //声明自定义函数///
    //这只是一个框架程序所以还没有自定义函数
    //函数的定义一般放在末尾

    //自定义类的声明定义///
    //这只是一个框架程序所以还没有自定义类
    //习惯类的声明定义同时进行

    //编写消息处理函数/
    //函数名可自定义,但形式固定//
    LRESULT CALLBACK WindowProc(HWND hwnd, //指定窗口句柄
              UINT msg, //消息代码
                                WPARAM wparam, //消息附加参数
                                LPARAM lparam)//消息附加参数
    //下面是消息处理函数的函数体,由使用者根据需要编写具体内容
    {

    PAINTSTRUCT   ps;   //声明PAINTSTRUCT结构体结构体,包含用于绘制窗口客户区的信息
    HDC     hdc; //声明一个窗口设备描述表,供调用

    //响应消息
    //这个switch语句是整个应用程序的中心,我们编写的绝大多数代码,都是通过响应消息来工作
    switch(msg)

                     //常见系统消息有以下几种
                     //WN_ACTIVATE 当窗口被激活或者成为一个焦点时传递
                     //WM_CLOSE  当窗口关闭时传递
                     //WM_CREATE  当窗口第一次创建时传递
                     //WM_DESTROY  当窗口可能要被破坏时传递
                     //WM_MOVE   当窗口移动时传递
                     //WM_MOUSEMOVE 当移动鼠标时传递
                     //WM_KEYUP  当松开一个键时传递
                     //WM_KEYDOWN  当按钮一下键时传递
                     //WM_TIMER  当发生定时程序事件时传递
                     //WM_USER   允许传递消息
                     //WM_PAINT  当一个窗口需重画时传递
                     //WM_QUIT  当Windows应用程序最后结束时传递
                     //WM_SIZE  当一个窗口改变大小时传递

    case WM_CREATE:// 当窗口第一次创建时传递
            {
            //这里根据需要编写对应消息的处理代码


       } break;//离开语句

    case WM_PAINT: //当一个窗口需要重画时
       {

       hdc =BeginPaint(hwnd,&ps);//准备指定的窗口来重绘并将窗口设备描述表放到一个结构体中 

            EndPaint(hwnd,&ps);//释放BeginPaint所占用的资源


          } break;//离开语句

    case WM_DESTROY: //当窗口可能要被破坏时传递
       {

       PostQuitMessage(0);//发送一个WM_QUIT消息给消息队列

          

       } break;//离开语句
        
    default://如果消息没有相对应的处理,使用默认的处理方式
       return (DefWindowProc(hwnd, msg, wparam, lparam));

        }


    return (0);

    }//消息处理函数结束

    //主函数
    //主函数参数由系统传入//
    int WINAPI WinMain( HINSTANCE hinstance,//windows为应用程序生成的实例句柄
          HINSTANCE hprevinstance,//此参数已经过时,不再使用
          LPSTR lpcmdline,//程序命令行参数
          int ncmdshow)//打开主程序窗口的方式
    {

    //主函数函数体//
    //声明所需数据

    WNDCLASSEX winclass; // 声明窗口类
    HWND    hwnd; // 声明窗口句柄标识
    MSG     msg;   // 声明消息结构体标识

    //定义窗口类///

    winclass.cbSize         = sizeof(WNDCLASSEX);//窗口类占用内存的大小
    winclass.style    = CS_DBLCLKS | CS_OWNDC | 
                              CS_HREDRAW | CS_VREDRAW;//窗口属性,常用以下几种:
                            //CS_HREDRAW 若移动或改变了窗口宽度,则刷新整个窗口
                            //CS_VREDRAW 若移动或改变了窗口高度,则刷新整个窗口
                            //CS_OWNDC  为该类中每个窗口分配一个单值的设备描述表
                            //CS_DBLCLKS 当用户双击鼠标时向窗口程序发送一个双击的信息,光标位于属于该类的窗口中
                            //CS_PARENTDC 在母窗口中设定一个子窗口的剪切区,以便于子窗口能够画在母窗口中
                            //CS_SAVEBITS 在一个窗口中保存用户图像,以便于在该窗口被遮住、移动时不必每次刷新屏幕
                            //CS_NOCLOSE 禁止系统菜单上的关闭命令

    winclass.lpfnWndProc = WindowProc;//窗口消息回调函数的指针,此函数从系统获得消息
    winclass.cbClsExtra   = 0;         //用于保存附加的运行时间,一般默认0
    winclass.cbWndExtra   = 0;         //用于保存附加的运行时间,一般默认0
    winclass.hInstance   = hinstance; //应用程序生成的实例句柄
    winclass.hIcon    = LoadIcon(NULL,IDI_HAND);//应用程序图标
                            //IDI_APPLICATION 默认图标
                            //IDI_ASTERISK   星号
                            //IDI_EXCLAMATION 惊叹号
                            //IDI_HAND     手形图标
                            //IDI_QUESTION   问号
                            //IDI_WINLOGO   Windows徽标
           
    winclass.hCursor   = LoadCursor(NULL,IDC_WAIT );//应用程序光标,
                            //IDC_ARROW   标准箭头
                            //IDC_APPSTARTING 标准箭头和小沙漏标
                            //IDC_CROSS  横标线
                            //IDC_IBEAM  文本I型标
                            //IDC_NO    带正斜线的圆圈
                            //IDC_SIZEALL 四向箭头
                            //IDC_SIZENESW 指向东北-西南方向的双向箭头
                            //IDC_SIZENS  指向南北方向的双向箭头
                            //IDC_SIZENWSE 指向东南-西北方向的双向箭头
                            //IDC_SIZEWE  指向东西方向的双向箭头
                            //IDC_UPARROW  垂直方向的箭头
                            //IDC_WAIT   沙漏

    winclass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);//窗口背景
                            //BLACK_BRUSH  黑色画笔
                            //WHITE_BRUSH  白色画笔
                            //GRAY_BRUSH  灰色画笔
                            //LTGRAY_BRUSH  淡灰色画笔
                            //DKGRAY_BRUSH  深灰色画笔
                            //HOLLOW_BRUSH 空心画笔
                            //NULL_BRUSH  无效(NULL)画笔 
                            //BLACK_PEN  黑色笔
                            //WHITE_PEN  白色笔
                            //NULL_PEN  无效(NULL)笔

    winclass.lpszMenuName = NULL;//菜单资源
    winclass.lpszClassName = "WINCLASS01";//窗口类名
    winclass.hIconSm        = LoadIcon(NULL, IDI_QUESTION);//窗口小图标
                            //IDI_APPLICATION 默认图标
                            //IDI_ASTERISK   星号
                            //IDI_EXCLAMATION 惊叹号
                            //IDI_HAND     手形图标
                            //IDI_QUESTION   问号
                            //IDI_WINLOGO   Windows徽标

    //注册窗口类
    if (!RegisterClassEx(&winclass))
    return(0);

    //创建窗口//
    if (!(hwnd = CreateWindowEx(WS_EX_TOPMOST, //窗口的高级特性WS_EX_TOPMOST
            "WINCLASS01", //窗口类名
              "最基本的C++ 窗口程序", //窗口标题
              WS_OVERLAPPEDWINDOW | WS_VISIBLE,//窗口式样

                        //窗口式样有
                        //WS_POPUP   弹出式窗口
                        //WS_OVERLAPPED 带有标题栏和边界的重叠式窗口,类似WS_TILED类型 
                        //WS_OVERLAPPEDWINDOW 具有WS_OVERLAPPED、WS_CAPTION、WS_SYSMENU、
                        //     和WS_THICKFRAME、WS_MAXIMIZEBOXWS_MINIMIZEBOX的重叠式窗口
                        //WS_VISIBLE  开始就可见的窗口
                        //WS_SYSMENU  标题栏上有窗口菜单的窗口
                        //WS_BORDER   有细线边界的窗口
                        //WS_CAPTION   有标题栏的窗口(包括WS_BORDER样式)
                        //WS_ICONIC    开始就最小化的窗口,类似WS_MINIMIZE样式
                        //WS_MAXIMIZE   开始就最大化的窗口
                        //WS_MAXIMIZEBOX   具有最大化按钮的窗口。不能和WS_EX_CONGTEXTHELP样式合并。WS_SYSMENU也必须指定
                        //MS_MINIMIZE 开始就最小化的窗口,类似WS_ICONIC样式
                        //WS_MINIMIZEOBX 具有最小化按钮的窗口。不能和WS_EX_CONGTEXITHELP样式合并。WS_SYSMENU也必须指定
                        //WS_POPUPWINDOW  带有WS_BORDER、WS_POPUP和WS_SYSMENU类型的弹出式窗口
                        //WS_SIZEBOX  一个窗口边界可以变化,和WS_THICKFRAME类型相同
                        //WS_HSCROLL  带有水平滚动条的窗口
                        //WS_VSCROLL  带有垂直滚动条的窗口

               100,100,     //窗口左上角坐标,默认值CW_USEDEFALT
              600,400,    //窗口大小,默认值CW_USEDEFALT 
              NULL,     //父窗口句柄
              NULL,     //菜单句柄 
              hinstance,//应用程序实例句柄 
              NULL))) //高级特征

    //显示窗口/
    ShowWindow(hwnd,//窗口句柄
         SW_SHOWNORMAL);//打开主程序窗口的方式,主要有以下几种:
          //SW_SHOWNORMAL   激活并显示一个窗口。将它恢复到原始尺寸和位置。第一次显示该窗口时,应用程序将指定该标志。 
             //SW_SHOW   激活一个窗口,并按当前尺寸和位置显示 
             //SW_HIDE   隐藏一个窗口,并激活另外一个窗口 
             //SW_MAXIMIZE   将指定的窗口最大化 
             //SW_MINIMIZE   将指定的窗口最小化 
             //SW_RESTORE   激活并显示一个窗口,将它恢复到原始尺寸和位置。恢复为最小化窗口时,应用程序必须指定该标志。 
             //SW_SHOWMAXIMIZED   激活一个窗口,并以最大化窗口显示 
             //SW_SHOWMINIMIZED   激活一个窗口,并以最小化窗口显示 
             //SW_SHOWMINNOACTIVE   以最小化窗口方式显示一个窗口,激活的窗口依然保持激活的状态 
             //SW_SHOWNA   以当前状态显示一个窗口,激活的窗口依然保持激活的状态   
             //SW_SHOWONACTIVATE   以上一次窗口尺寸和位置来显示窗口,激活的窗口依然保持激活的状态

    //刷新窗口
    UpdateWindow(hwnd);//刷新窗口

    //从消息列队中取出消息,只有收到WM_QUIT消息时,GetMessage才返回0,循环终止/
    while(GetMessage(&msg,//消息结构体,用于保存获得的消息
       NULL,//指定所接收消息对应的窗口句柄
       0,0))//指定所接收消息的最小值和最大值
       { 
    // 把虚拟键消息转换成字符消息
    TranslateMessage(&msg);

    // 把应用程序消息回传系统
    DispatchMessage(&msg);
    }


    return(msg.wParam);//返回WM_QUIT消息的wParam值

    } //主函数结束//

    //定义自定义函数//
    //这只是一个框架程序所以还没有自定义函数

    //程序源代码结束//

    展开全文
  • 创建属于自己的窗口 ================== ·创建窗口前的准备 在创建窗口前我们先来熟悉几个名词, 这些名词现在可以暂时去透彻的进行理解, 只需要印象中知道有
    原文地址为:
    C语言Windows程序设计->第三天->属于自己的窗口
    

     

    创建属于自己的窗口

    ==================

     

    ·创建窗口前的准备

     

    在创建窗口前我们先来熟悉几个名词, 这些名词现在可以暂时不去透彻的进行理解, 只需要印象中知道有这么回事就行。

     

    1>. 窗口

      "窗口"这个我们都已经十分熟悉了, 在Windows中, 一个应用程序窗口上一般会在标题栏上显示程序的名称, 紧挨着标题栏的菜单栏, 或许还会有状态栏、滚动条等其他"装饰品"。

     

    2>. 控件 

      在一些应用程序中, 我们经常可以在程序的界面上(窗口)看到一些按钮(Push Button)、文本框(Text Box)、列表框(List Box)、滚动条(Scroll Bar)等, 这些对象通常被称为控件, 在《Windows程序设计》一书中, 还被称为"子窗口"、"控件窗口"或"子窗口控件"。

     

    3>. 窗口类

      在建立一个窗口前, 我们必须首先注册一个"窗口类"(Windows Class), 接触过面向对象的朋友应该会首先想到面向对象当中的"类", 但是, 这里"窗口类"中的"类"并不是指面向对象当中的那个"类"。

      在这里我们可以把"窗口类"理解为一个结构体, 结构体的成员就是窗口的一些属性, 例如窗口的标题是什么、窗口使用什么样的小图标以及窗口的风格之类的属性, 一个窗口就是一个结构体的对象, 结构体成员的属性决定着窗口的属性。

     

    4>. 消息循环

      在Windows程序设计中, 消息循环是个不得不提的概念, Windows操作系统是以消息驱动的, 消息队列是指在一个应用程序运行时, Windows操作系统会为该应用程序建立一个"消息队列", 这个消息队列用来存放该程序可能创建的各种窗口的消息, 当用户对应用程序进行操作时, 例如点击一个按钮、调整下窗口的大小等, 此时Windows会立即把这一消息告诉应用程序, 使应用程序能作出相应的动作。

      笔者觉得有比较强调一下上一段中的最后一句"此时Windows会立即把这一消息告诉应用程序, 使应用程序能作出相应的动作。", 我们在创建一个应用程序时, 不用想着什么时候才能从用户那得到指令, 因为操作系统会即时告诉我们用户此时是否对程序进行了操作, 对于没有接触过Windows编程的朋友们这点可能有点难以理解, 简单来说就是像如何获取一个按钮是否被单击, 或者如何获取用户此时是否在调整窗口大小之类的代码Windows已经帮我们完成了, 我们只需要等待着Windows给我们发消息就行, 如何判断消息类型以及处理这些消息, 在代码上通常我们用while配合一个巨大的switch来完成,。

     

    5>. 窗口的过程函数

      当一个窗口建立之后, 就可以从Windows那里不断的接收到Windows发来的消息, 接收到消息后我们就需要一个函数来处理这些消息, 可以简单的理解为, 这个用来处理接收到的消息的函数就称为窗口过程函数或者回调函数。

     

    下面我们来尝试着看一个创建一个基本窗口的代码。

     

    ·尝试创建自己的窗口

    代码如下:

    #include <windows.h>

    LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ) ;
    //声明用来处理消息的函数

    int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow )
    {
    static TCHAR szAppName[] = TEXT("MyWindow") ;
    HWND hwnd ;
    MSG msg ;
    WNDCLASS wndclass ;
    //声明一个窗口类对象

    //以下为窗口类对象wndclass的属性
    wndclass.style = CS_HREDRAW | CS_VREDRAW ; //窗口样式
    wndclass.lpszClassName = szAppName ; //窗口类名
    wndclass.lpszMenuName = NULL ; //窗口菜单:无
    wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH) ; //窗口背景颜色
    wndclass.lpfnWndProc = WndProc ; //窗口处理函数
    wndclass.cbWndExtra = 0 ; //窗口实例扩展:无
    wndclass.cbClsExtra = 0 ; //窗口类扩展:无
    wndclass.hInstance = hInstance ; //窗口实例句柄
    wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION ) ; //窗口最小化图标:使用缺省图标
    wndclass.hCursor = LoadCursor( NULL, IDC_ARROW ) ; //窗口采用箭头光标

    if( !RegisterClass( &wndclass ) )
    {
    //注册窗口类, 如果注册失败弹出错误提示
    MessageBox( NULL, TEXT("窗口注册失败!"), TEXT("错误"), MB_OK | MB_ICONERROR ) ;
    return 0 ;
    }

    hwnd
    = CreateWindow( //创建窗口
    szAppName, //窗口类名
    TEXT("我的窗口"), //窗口标题
    WS_OVERLAPPEDWINDOW, //窗口的风格
    CW_USEDEFAULT, //窗口初始显示位置x:使用缺省值
    CW_USEDEFAULT, //窗口初始显示位置y:使用缺省值
    CW_USEDEFAULT, //窗口的宽度:使用缺省值
    CW_USEDEFAULT, //窗口的高度:使用缺省值
    NULL, //父窗口:无
    NULL, //子菜单:无
    hInstance, //该窗口应用程序的实例句柄
    NULL //
    ) ;

    ShowWindow( hwnd, iCmdShow ) ;
    //显示窗口
    UpdateWindow( hwnd ) ; //更新窗口

    while( GetMessage( &msg, NULL, 0, 0 ) ) //从消息队列中获取消息
    {
    TranslateMessage(
    &msg ) ; //将虚拟键消息转换为字符消息
    DispatchMessage( &msg ) ; //分发到回调函数(过程函数)
    }
    return msg.wParam ;
    }

    LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
    {
    HDC hdc ;
    //设备环境句柄
    PAINTSTRUCT ps ; //绘制结构
    RECT rect; //矩形结构

    switch( message ) //处理得到的消息
    {
    case WM_CREATE: //窗口创建完成时发来的消息
    MessageBox( hwnd, TEXT("窗口已创建完成!"), TEXT("我的窗口"), MB_OK | MB_ICONINFORMATION ) ;
    return 0;

    case WM_PAINT: //处理窗口区域无效时发来的消息
    hdc = BeginPaint( hwnd, &ps ) ;
    GetClientRect( hwnd,
    &rect ) ;
    DrawText( hdc, TEXT(
    "Hello, 这是我自己的窗口!" ), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER ) ;
    EndPaint( hwnd,
    &ps ) ;
    return 0 ;

    case WM_LBUTTONDOWN: //处理鼠标左键被按下的消息
    MessageBox( hwnd, TEXT("鼠标左键被按下。"), TEXT("单击"), MB_OK | MB_ICONINFORMATION ) ;
    return 0;

    case WM_DESTROY: //处理窗口关闭时的消息
    MessageBox( hwnd, TEXT("关闭程序!"), TEXT("结束"), MB_OK | MB_ICONINFORMATION ) ;
    PostQuitMessage(
    0 ) ;
    return 0;
    }
    return DefWindowProc( hwnd, message, wParam, lParam ) ; //DefWindowProc处理我们自定义的消息处理函数没有处理到的消息
    }

    --------------------

    --------------------

      先简单介绍这段代码, 在主函数WinMain中的窗口类对象wndclass我们定义了窗口的相关属性, 尝试注册窗口类并调用CreateWindow创建窗口, 创建完成后用ShowWindow让窗口显示出来, 我们还使用了个while用来从消息队列里获取并分发消息给程序, 我们还定义了一个过程函数WndProc用来处理系统发来的消息。

     

      编译运行后, 如果没有错误将首先看到一个窗口建立成功与否的对话框, 当创建窗口失败时弹出一个错误对话框并关闭程序。

     

      当窗口被创建时首先Windows会发给我们一条创建完成的消息"WM_CREATE", 在我们的消息处理函数WinProc中有switch-case语句对该消息进行了处理, 就是弹出一个窗口创建完成的对话框。

     

     

      同样, 我们还处理了当窗口的客户区(内容部分)被改变时发来的的"WM_PAINT"消息进行了处理, 让"Hello, 这是我创建的窗口!"始终显示在窗口中心。

    "WM_LBUTTONDOWN"消息是当用户在客户区按下鼠标左键时发来的消息, 我们作出相应的动作为弹出一个鼠标左键被按下的对话框。

     

     

    当用户点击窗口的关闭按钮时, "WM_DESTROY"消息就会发来, 我们作出的动作是弹出"关闭程序!"的对话框并且退出程序。

     

     

    今天的学习先到这里, 明天我们详细学习下整个创建窗口的代码。

    --------------------

    Wid, 2012.10.08


    转载请注明本文地址: C语言Windows程序设计->第三天->属于自己的窗口
    展开全文
  • 很多次使用弹出窗口作为提示的窗口之用,但是还真的没有认真的去研究一下,今天写程序的时候,希望不仅提示用户话,还加上一个警告的小窗口,顺便也研究一下如何去使用,好啦,现在开始吧。tEjSSE6 工作室网络技术...

    很多次使用弹出窗口作为提示的窗口之用,但是还真的没有认真的去研究一下,今天写程序的时候,希望不仅提示用户话,还加上一个警告的小窗口,顺便也研究一下如何去使用,好啦,现在开始吧。tEjSSE6 工作室网络技术空间
    VC函数原型:tEjSSE6 工作室网络技术空间
    int MessageBox(          tEjSSE6 工作室网络技术空间
        HWND hWnd,tEjSSE6 工作室网络技术空间
        LPCTSTR lpText,tEjSSE6 工作室网络技术空间
        LPCTSTR lpCaption,tEjSSE6 工作室网络技术空间
        UINT uTypetEjSSE6 工作室网络技术空间
    );tEjSSE6 工作室网络技术空间

    VC函数参数说明:tEjSSE6 工作室网络技术空间
        hWnd:标识将被创建的消息框的拥有窗口。如果此参数为NULL,则消息框没有拥有窗口。tEjSSE6 工作室网络技术空间
        lpText:指向一个以NULL结尾的、含有将被显示的消息的字符串的指针。tEjSSE6 工作室网络技术空间
        lpCaption:指向一个以NULL结尾的、用于对话框标题的字符串的指针。tEjSSE6 工作室网络技术空间
        uType:指定一个决定对话框的内容和行为的位标志集。此参数可以为下列标志组中标志的组合。tEjSSE6 工作室网络技术空间
            指定下列标志中的一个来显示消息框中的按钮,标志的含义如下。tEjSSE6 工作室网络技术空间
                MB_ABORTRETRYIGNORE:消息框含有三个按钮:Abort,Retry和Ignore。tEjSSE6 工作室网络技术空间
         MB_CANCELTRYCONTINUE:Microsoft Windows 2000/XP平台下所使用的提示,有三个按钮Cancel, Try Again, Continue.这个按钮 主要是用来代替MB_ABORTRETRYIGNORE。tEjSSE6 工作室网络技术空间
         MB_HELP:把一个Help按钮增加到消息框。选择Help按钮或按F1产生一个Help事件。tEjSSE6 工作室网络技术空间
         MB_OK:消息框含有一个按钮:OK。这是缺省值。tEjSSE6 工作室网络技术空间
              MB_OKCANCEL:消息框含有两个按钮:OK和Cancel。tEjSSE6 工作室网络技术空间
         MB_RETRYCANCEL:消息框含有两个按钮:Retry和Cancel。tEjSSE6 工作室网络技术空间
         MB_YESNO:消息框含有两个按钮:Yes和No。tEjSSE6 工作室网络技术空间
         MB_YESNOCANCEL:消息框含有三个按钮:Yes,No和Cancel。tEjSSE6 工作室网络技术空间
            指定下列标志中的一个来显示消息框中的图标:标志的含义如下。tEjSSE6 工作室网络技术空间
         MB_ICONEXCLAMATION:一个惊叹号出现在消息框。tEjSSE6 工作室网络技术空间
         MB_ICONWARNING:一个惊叹号出现在消息框。tEjSSE6 工作室网络技术空间
         MB_ICONINFORMATION:一个圆圈中小写字母i组成的图标出现在消息框。tEjSSE6 工作室网络技术空间
         MB_ICONASTERISK:一个圆圈中小写字母i组成的图标出现在消息框。tEjSSE6 工作室网络技术空间
         MB_ICONQUESTION:一个问题标记图标出现在消息框。tEjSSE6 工作室网络技术空间
         MB_ICONSTOP:一个停止消息图标出现在消息框。tEjSSE6 工作室网络技术空间
         MB_ICONERROR:一个停止消息图标出现在消息框。tEjSSE6 工作室网络技术空间
                MB_ICONHAND:一个停止消息图标出现在消息框。tEjSSE6 工作室网络技术空间
            指定下列标志中的一个来显不缺省的按钮:标志的含义如下。tEjSSE6 工作室网络技术空间
         MB_DEFBUTTON1:第一个按钮为缺省按钮。如果MB_DEFBUTTON2,MB_DEFBUTTON3,MB_DEFBUTTON4没有被指定,则MB_DEFBUTTON1为缺省值。tEjSSE6 工作室网络技术空间
              MB_DEFSUTTON2;第二个按钮为缺省按钮。tEjSSE6 工作室网络技术空间
              MB_DEFBUTTON3:第三个按钮为缺省按钮。tEjSSE6 工作室网络技术空间
              MB_DEFBUTTON4:第四个按钮为缺省按钮。tEjSSE6 工作室网络技术空间
     指定下列标志中的一个来显示对话框的形态:标志的含义如卜。tEjSSE6 工作室网络技术空间
         MB_APPLMODAL:在hwnd参数标识的窗口中继续工作以前,用户一定响应消息框。但是,用户可以移动到其他线程的窗口且在这些窗口中工作。根据应用程序中窗口的层次机构,用户则以移动到线程内的其他窗口。所有母消息框的子窗口自动地失效,但是弹出窗口不是这样。如果既没有指定MB_SYSTEMMODAL也没有指定MB_TASKMOOAL,则MB_APPLMODAL为缺省的。tEjSSE6 工作室网络技术空间
           MB_SYSTEMMODAL:除了消息框有WB_EX_TOPMOST类型,MB_APPLMODAL和WS_EX_TOPMOST一样。用系统模态消息框来改变各种各样的用户,主要的损坏错误需要立即注意(例如,内存溢出)。如果不是那些与hwnd联系的窗口,此标志对用户对窗口的相互联系没有影响。tEjSSE6 工作室网络技术空间
              MB_TASKMODAL:如果参数hwnd为NULL,除了所有属于当前线程高层次的窗口足失效的,MB_TASKMODALL和MB_ApPLMODAL一样。当调用应用程序或库没有一个可以得到的窗口句柄时,使用此标志。但仍需要阻止到调用应用程序甲其他窗口的输入而不是搁置其他线程。tEjSSE6 工作室网络技术空间
     另外,可以指定下列标志。tEjSSE6 工作室网络技术空间
           MB_DEFAULT_DESKTOP_ONLy:接收输入的当前桌面一定是一个缺省桌面。否则,函数调用失败。缺省桌面是一个在用户已经纪录且以后应用程序在此上面运行的桌面。tEjSSE6 工作室网络技术空间
              MB_HELP:把一个Help按钮增加到消息框。选择Help按钮或按F1产生一个Help事件。tEjSSE6 工作室网络技术空间
              MB_RIGHT:文本为右调整。tEjSSE6 工作室网络技术空间
              MB_RTLREADING:用在Hebrew和Arabic系统中从右到左的顺序显示消息和大写文本。tEjSSE6 工作室网络技术空间
              MB_SETFOREGROUND:消息框变为前景窗口。在内部系统为消息个调用SetForegrundWindow函数。tEjSSE6 工作室网络技术空间
              MB_TOPMOSI:消息框用WS_EX_TOPMOST窗口类型来创建MB_SERVICE_NOTIFICATION。tEjSSE6 工作室网络技术空间
              Windows NT:调用程序是一个通知事件的用户的服务程序。函数在当前活动桌面上显示一个消息框,即使没有用户登记到计算机。tEjSSE6 工作室网络技术空间

    VC函数返回值:tEjSSE6 工作室网络技术空间
        如果程序返回的是失败的话,会返回0,否则会返回相应的数值。tEjSSE6 工作室网络技术空间
        IDABORT 终止按钮被选择tEjSSE6 工作室网络技术空间
        IDCANCEL 取消的按钮被选择tEjSSE6 工作室网络技术空间
        IDCONTINUE 继续的按钮被选择 tEjSSE6 工作室网络技术空间
        IDIGNORE 忽略的按钮被选择tEjSSE6 工作室网络技术空间
        IDNO 否的按钮被选择tEjSSE6 工作室网络技术空间
        IDOK 确定的按钮被选择tEjSSE6 工作室网络技术空间
        IDRETRY 重试的按钮被选择tEjSSE6 工作室网络技术空间
        IDTRYAGAIN 重试一次的按钮被选择tEjSSE6 工作室网络技术空间
        IDYES 是的按钮被选择tEjSSE6 工作室网络技术空间

    tEjSSE6 工作室网络技术空间
    VC函数源代码:tEjSSE6 工作室网络技术空间
    if (fError) tEjSSE6 工作室网络技术空间
    { tEjSSE6 工作室网络技术空间
        if (MessageBox(hwndDlg, SZNOTFOUND, SZDELETEITEM, tEjSSE6 工作室网络技术空间
            MB_OKCANCEL)==IDOK) tEjSSE6 工作室网络技术空间
        {tEjSSE6 工作室网络技术空间

             // Prompt for a new item name and repeat the command. tEjSSE6 工作室网络技术空间

        }tEjSSE6 工作室网络技术空间
        else tEjSSE6 工作室网络技术空间
        {tEjSSE6 工作室网络技术空间

             // Cancel the command. tEjSSE6 工作室网络技术空间

        }tEjSSE6 工作室网络技术空间
    } tEjSSE6 工作室网络技术空间
    tEjSSE6 工作室网络技术空间

    相关网址tEjSSE6 工作室网络技术空间
    1. VC2005提示MFC80UD.DLL未找到的解决办法    http://js.sse6.cn/gzsgfrz/jszj/2009-11-29/1154.htmltEjSSE6 工作室网络技术空间
    2. VC编程:弹出窗口函数MessageBox用法、分析及相应源代码    http://js.sse6.cn/gzsgfrz/jszj/2009-11-29/1155.htmltEjSSE6 工作室网络技术空间
    3. VC编程:获取系统毫秒级时间函数GetTickCount用法、分析及相应源代码    http://js.sse6.cn/gzsgfrz/jszj/2009-11-29/1156.htmltEjSSE6 工作室网络技术空间
    4. VC编程:获取系统进程句柄OpenProcess 用法、分析及相应源代码    http://js.sse6.cn/gzsgfrz/jszj/2009-11-29/1157.htmltEjSSE6 工作室网络技术空间
    5. VC编程:关闭进程TerminateProcess分析    http://js.sse6.cn/gzsgfrz/jszj/2009-11-29/1158.htmltEjSSE6 工作室网络技术空间

    本文源自:http://js.sse6.cn/ 转载请注明。tEjSSE6 工作室网络技术空间

    展开全文
  • 与Activity类似,Android系统中的窗口也是以堆栈的形式组织在WindowManagerService服务中的,其中,Z轴位置较低的窗口位于Z轴位置较高的窗口的下面。在本文中,我们就详细分析WindowManagerService服务是如何以堆栈...

            我们知道,在Android系统中,Activity是以堆栈的形式组织在ActivityManagerService服务中的。与Activity类似,Android系统中的窗口也是以堆栈的形式组织在WindowManagerService服务中的,其中,Z轴位置较低的窗口位于Z轴位置较高的窗口的下面。在本文中,我们就详细分析WindowManagerService服务是如何以堆栈的形式来组织窗口的。

    《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

            从前面Android应用程序启动过程源代码分析一文可以知道,应用程序进程中的每一个Activity组件在Activity管理服务ActivityManagerService中都对应有一个ActivityRecord对象。从前面Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析一文又可以知道,Activity管理服务ActivityManagerService中每一个ActivityRecord对象在Window管理服务WindowManagerService中都对应有一个AppWindowToken对象。

            此外,在输入法管理服务InputMethodManagerService中,每一个输入法窗口都对应有一个Binder对象,这个Binder对象在Window管理服务WindowManagerService又对应有一个WindowToken对象。

            与输入法窗口类似,在壁纸管理服务WallpaperManagerService中,每一个壁纸窗口都对应有一个Binder对象,这个Binder对象在Window管理服务WindowManagerService也对应有一个WindowToken对象。

            在Window管理服务WindowManagerService中,无论是AppWindowToken对象,还是WindowToken对象,它们都是用来描述一组有着相同令牌的窗口的,每一个窗口都是通过一个WindowState对象来描述的。例如,一个Activity组件窗口可能有一个启动窗口(Starting Window),还有若干个子窗口,那么这些窗口就会组成一组,并且都是以Activity组件在Window管理服务WindowManagerService中所对应的AppWindowToken对象为令牌的。从抽象的角度来看,就是在Window管理服务WindowManagerService中,每一个令牌(AppWindowToken或者WindowToken)都是用来描述一组窗口(WindowState)的,并且每一个窗口的子窗口也是与它同属于一个组,即都有着相同的令牌。

            上述的窗口组织方式如图1所示:


    图1 窗口在WindowManagerService服务中的组织方式

            其中,Activity Stack是在ActivityManagerService服务中创建的,Token List和Window Stack是在WindowManagerService中创建的,而Binder for IM和Binder for WP分别是在InputMethodManagerService服务和WallpaperManagerService服务中创建的,用来描述一个输入法窗口和一个壁纸窗口。

            图1中的对象的对应关系如下所示:

           1. ActivityRecord-J对应于AppWindowToken-J,后者描述的一组窗口是{WindowState-A, WindowState-B, WindowState-B-1},其中, WindowState-B-1是WindowState-B的子窗口。

           2. ActivityRecord-K对应于AppWindowToken-K,后者描述的一组窗口是{WindowState-C, WindowState-C-1, WindowState-D, WindowState-D-1},其中, WindowState-C-1是WindowState-C的子窗口,WindowState-D-1是WindowState-D的子窗口。

           3. ActivityRecord-N对应于AppWindowToken-N,后者描述的一组窗口是{WindowState-E},其中, WindowState-E是系统当前激活的Activity窗口。

           4. Binder for IM对应于WindowToken-I,后者描述的一组窗口是{WindowState-I},其中, WindowState-I是WindowState-E的输入法窗口。

           5. Binder for WP对应于WindowToken-W,后者描述的一组窗口是{WindowState-W},其中, WindowState-W是WindowState-E的壁纸窗口。

           从图1还可以知道,Window Stack中的WindowState是按照它们所描述的窗口的Z轴位置从低到高排列的。

           以上就是WindowManagerService服务组织系统中的窗口的抽象模型,接下来我们将分析AppWindowToken、WindowToken和WindowState的一些增加、移动和删除等操作,以便可以对这个抽象模型有一个更深刻的认识。

           1.  增加AppWindowToken

           从前面Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析一文可以知道,一个Activity组件在启动的过程中,ActivityManagerService服务会调用调用WindowManagerService类的成员函数addAppToken来为它增加一个AppWindowToken,如下所示:

    public class WindowManagerService extends IWindowManager.Stub  
            implements Watchdog.Monitor {  
        ......  
      
        /** 
         * Mapping from a token IBinder to a WindowToken object. 
         */  
        final HashMap<IBinder, WindowToken> mTokenMap =  
                new HashMap<IBinder, WindowToken>();  
      
        /** 
         * The same tokens as mTokenMap, stored in a list for efficient iteration 
         * over them. 
         */  
        final ArrayList<WindowToken> mTokenList = new ArrayList<WindowToken>();  
        ......  
      
        /** 
         * Z-ordered (bottom-most first) list of all application tokens, for 
         * controlling the ordering of windows in different applications.  This 
         * contains WindowToken objects. 
         */  
        final ArrayList<AppWindowToken> mAppTokens = new ArrayList<AppWindowToken>();  
        ......  
      
        public void addAppToken(int addPos, IApplicationToken token,  
                int groupId, int requestedOrientation, boolean fullscreen) {  
            ......  
      
            synchronized(mWindowMap) {  
                AppWindowToken wtoken = findAppWindowToken(token.asBinder());  
                if (wtoken != null) {  
                    ......  
                    return;  
                }  
                wtoken = new AppWindowToken(token);  
                ......  
                mAppTokens.add(addPos, wtoken);  
                ......  
                mTokenMap.put(token.asBinder(), wtoken);  
                mTokenList.add(wtoken);  
      
                ...... 
            }  
        }  
      
        ......  
    }  
           这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

           WindowManagerService类有三个成员变量mTokenMap、mTokenList和mAppTokens,它们都是用来描述系统中的窗口的。

           成员变量mTokenMap指向的是一个HashMap,它里面保存的是一系列的WindowToken对象,每一个WindowToken对象都是用来描述一个窗口的,并且是以描述这些窗口的一个Binder对象的IBinder接口为键值的。例如,对于Activity组件类型的窗口来说,它们分别是以用来描述它们的一个ActivityRecord对象的IBinder接口保存在成员变量mTokenMap所指向的一个HashMap中的。

           成员变量mTokenList指向的是一个ArrayList,它里面保存的也是一系列WindowToken对象,这些WindowToken对象与保存在成员变量mTokenMap所指向的一个HashMap中的WindowToken对象是一样的。成员变量mTokenMap和成员变量mTokenList的区别就在于,前者在给定一个IBinder接口的情况下,可以迅速指出是否存在一个对应的WindowToken对象,而后者可以迅速遍历WindowManagerService服务中的WindowToken对象。

           成员变量mAppTokens指向的也是一个ArrayList,不过它里面保存的是一系列AppWindowToken对象,每一个AppWindowToken对象都是用来描述一个Activity组件窗口的,而这些AppWindowToken对象是以它们描述的窗口的Z轴坐标由小到大保存在这个ArrayList中的,这样我们就可以通过这个ArrayList来从上到下或者从下到上地遍历系统中的所有Activity组件窗口。由于这些AppWindowToken对象所描述的Activity组件窗口也是一个窗口,并且AppWindowToken类是从WindowToken继承下来的,因此,这些AppWindowToken对象还会同时被保存在成员变量mTokenMap所指向的一个HashMap和成员变量mTokenList所指向的一个ArrayList中。

           理解了WindowManagerService类的这三个成员变量的含义之后,它的成员函数addAppToken的实现就好理解了,其中,参数token指向的便是用来描述正在启动的Activity组件所对应的一个ActivityRecord对象,而参数addPos用来描述该Activity组件在堆栈中的位置,这个位置同时也是接下来要创建的AppWindowToken对象在WindowManagerService类的mTokenList所描述的一个ArrayList中的位置。

           WindowManagerService类的成员函数addAppToken首先调用另外一个成员函数findAppWindowToken来在成员变量mTokenMap所描述的一个HashMap检查是否已经存在一个AppWindowToken。如果已经存在的话,那么WindowManagerService类的成员函数addAppToken就什么也不做就返回了,否则的话,就会使用参数token来创建一个AppWindowToken对象,并且会将该AppWindowToken对象分别保存在WindowManagerService类的成员变量mTokenMap、mTokenList和mAppTokens中。

           2. 删除AppWindowToken

           删除AppWindowToken是通过调用WindowManagerService类的成员函数removeAppTokensLocked来实现的,如下所示:

    public class WindowManagerService extends IWindowManager.Stub  
            implements Watchdog.Monitor {  
        ......  
      
        private void removeAppTokensLocked(List<IBinder> tokens) {
            // XXX This should be done more efficiently!
            // (take advantage of the fact that both lists should be
            // ordered in the same way.)
            int N = tokens.size();
            for (int i=0; i<N; i++) {
                IBinder token = tokens.get(i);
                final AppWindowToken wtoken = findAppWindowToken(token);
                if (!mAppTokens.remove(wtoken)) {
                    ......
                    i--;
                    N--;
                }
            }
        }
     
        ......  
    }  
            这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

            WindowManagerService类的成员函数removeAppTokensLocked可以同时删除一组AppWindowToken对象。

            参数tokens所描述的是一个IBinder接口列表,与这些IBinder接口所对应的AppWindowToken对象就是接下来要删除的。WindowManagerService类的成员函数removeAppTokensLocked通过一个for循环来依次调用另外一个成员函数findAppWindowToken,以便可以找到保存在列表tokens中的每一个IBinder接口所对应的AppWindowToken对象,然后将该AppWindowToken对象从WindowManagerService类的成员变量mAppTokens所描述的一个ArrayList中删除。

            注意,WindowManagerService类的成员函数removeAppTokensLocked是在内部使用的,它只是把一个AppWindowToken对象从成员变量mAppTokens中删除,而没有从另外两个成员变量mTokenMap和mTokenList中删除。

            3. 移动AppWindowToken至指定位置

            移动AppWindowToken至指定位置是通过调用WindowManagerService类的成员函数moveAppToken来实现的,如下所示:

    public class WindowManagerService extends IWindowManager.Stub  
            implements Watchdog.Monitor {  
        ......  
      
        public void moveAppToken(int index, IBinder token) {
            if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                    "moveAppToken()")) {
                throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
            }
    
            synchronized(mWindowMap) {
                ......
                final AppWindowToken wtoken = findAppWindowToken(token);
                if (wtoken == null || !mAppTokens.remove(wtoken)) {
                    ......
                    return;
                }
                mAppTokens.add(index, wtoken);
                ......
    
                final long origId = Binder.clearCallingIdentity();
                ......
                if (tmpRemoveAppWindowsLocked(wtoken)) {
                    ......
                    reAddAppWindowsLocked(findWindowOffsetLocked(index), wtoken);
                    ......
                    updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES);
                    mLayoutNeeded = true;
                    performLayoutAndPlaceSurfacesLocked();
                }
                Binder.restoreCallingIdentity(origId);
            }
        }
     
        ......  
    }  
            这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

            参数token描述的是要移动的AppWindowToken对象所对应的一个IBinder接口,而参数index描述的是该AppWindowToken对象要移动到的位置。注意,移动一个AppWindowToken对象到指定的位置是需要android.Manifest.permission.MANAGE_APP_TOKENS权限的。

            WindowManagerService类的成员函数moveAppToken首先找到与参数token所对应的AppWindowToken对象,并且将该AppWindowToken对象从WindowManagerService类的成员变量mAppTokens所描述的一个ArrayList中移除,这样做的目的是为了接下来可以将该AppWindowToken对象移动至该ArrayList中的指定位置上,即参数index所描述的位置上。

            注意,上述操作只是将参数token所对应的AppWindowToken对象移动到了WindowManagerService类的成员变量mAppTokens所描述的一个ArrayList的指定位置上,接下来还需要同时将与该AppWindowToken对象所对应的WindowState对象移动至WindowManagerService服务内部的一个WindowState堆栈合适位置上去。

            移动对应的WindowState对象的操作同样也是分两步执行的:第一步先调用WindowManagerService类的成员函数tmpRemoveAppWindowsLocked来将这些WindowState对象从原来的WindowState堆栈位置移除;第二步再调用WindowManagerService类的成员函数reAddAppWindowsLocked来将这些WindowState对象插入到WindowState堆栈的合适位置去。

            对应的WindowState对象被移动到的合适位置是通过调用WindowManagerService类的成员函数findWindowOffsetLocked来获得的,它的实现如下所示:

    public class WindowManagerService extends IWindowManager.Stub  
            implements Watchdog.Monitor {  
        ......  
    
        /**
         * Z-ordered (bottom-most first) list of all Window objects.
         */
        final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();
        ......
      
        private int findWindowOffsetLocked(int tokenPos) {
            final int NW = mWindows.size();
    
            if (tokenPos >= mAppTokens.size()) {
                int i = NW;
                while (i > 0) {
                    i--;
                    WindowState win = mWindows.get(i);
                    if (win.getAppToken() != null) {
                        return i+1;
                    }
                }
            }
    
            while (tokenPos > 0) {
                // Find the first app token below the new position that has
                // a window displayed.
                final AppWindowToken wtoken = mAppTokens.get(tokenPos-1);
                ......
                if (wtoken.sendingToBottom) {
                    ......
                    tokenPos--;
                    continue;
                }
                int i = wtoken.windows.size();
                while (i > 0) {
                    i--;
                    WindowState win = wtoken.windows.get(i);
                    int j = win.mChildWindows.size();
                    while (j > 0) {
                        j--;
                        WindowState cwin = win.mChildWindows.get(j);
                        if (cwin.mSubLayer >= 0) {
                            for (int pos=NW-1; pos>=0; pos--) {
                                if (mWindows.get(pos) == cwin) {
                                    ......
                                    return pos+1;
                                }
                            }
                        }
                    }
                    for (int pos=NW-1; pos>=0; pos--) {
                        if (mWindows.get(pos) == win) {
                            ......
                            return pos+1;
                        }
                    }
                }
                tokenPos--;
            }
    
            return 0;
        }
     
        ......  
    }  
            这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

            参数tokenPos描述的是一个AppWindowToken对象在WindowManagerService类的成员变量mAppTokens所描述的一个ArrayList的位置,WindowManagerService类的成员函数findWindowOffsetLocked的目标就要找到与该AppWindowToken对象所对应的WindowState对象在WindowManagerService服务内部的一个WindowState堆栈的起始偏移位置。有了这个起始偏移位置之后,我们就可以将对应的所有WindowState对象有序地插入到该WindowState堆栈中去。WindowManagerService服务内部的WindowState堆栈是通过WindowManagerService类的成员变量mWindows来描述的。接下来我们就分两种情况来分析这个起始偏移位置的计算过程。

            第一种情况是参数tokenPos的值大于WindowManagerService类的成员变量mAppTokens所描述的一个ArrayList的大小。这是一种异常情况,一般来说,参数tokenPos是指向mAppTokens列表的某一个位置的,不过这时候意味着它所描述的AppWindowToken对象的Z轴位置要大于mAppTokens列表的最上面的一个AppWindowToken对象的Z轴位置的。这也就是说,与参数tokenPos所描述的AppWindowToken对象所对应的WindowState对象的要位于与mAppTokens列表的最上面的一个AppWindowToken对象所对应的任一个WindoState对象的上面。因此,就需要找到与mAppTokens列表的最上面的一个AppWindowToken对象所对应的Z轴位置最大的一个WindoState对象在WindowState堆栈中的位置i,然后就可以知道与参数tokenPos所描述的AppWindowToken对象所对应的WindowState对象在WindowState堆栈的起始偏移位置为i+1。

            如何找到mAppTokens列表的最上面的一个AppWindowToken对象所对应的Z轴位置最大的一个WindoState对象在WindowState堆栈中的位置i呢?从图1可以可得到一个结论:WindowManagerService服务内部中的所有WindowState对象都是按照Z轴从位置从小到大排列在WindowState堆栈中的,并且在mAppTokens列表中,位于上面的一个AppWindowToken对象所对应的那些WindowState对象的Z轴位置是一定大于位于下面的一个AppWindowToken对象所对应的那些WindowState对象的Z轴位置的。因此,我们只要从WindowState堆栈的顶端开始往下遍历,找到这样的一个WindowState对象,它是属于一个AppWindowToken对象的,即它的成员函数getAppToken的返回值不等于null,那么它在WindowState堆栈中的位置就是我们要找到的位置i。有了这个位置i之后,将它的值加上1,就可以得到参数t所描述的AppWindowToken对象所对应的WindowState对象在WindowState堆栈的起始偏移位置了。

            第二种情况是参数tokenPos的值小于WindowManagerService类的成员变量mAppTokens所描述的一个ArrayList的大小。根据前面得到的推论,我们只要在mAppTokens列表中找到一个AppWindowToken对象,它满足以下三个条件:

            A. 它在mAppTokens列表中的位置小于tokenPos;

            B. 它在WindowState堆栈中对应有WindowState对象;

            C. 它不是将要置于WindowState堆栈的底部。

            如果一个AppWindowToken对象在WindowState堆栈中对应有WindowState对象,那么这些WindowState对象也会同时按照Z轴从小到大的顺序保存它的成员变量windows所描述的一个ArrayList中,这意味着如果一个AppWindowToken对象满足条件B,那么它的成员变量windows所描述的一个ArrayList的大小就大于0。

            如果一个AppWindowToken对象不是将要置于WindowState堆栈的底部,那么它的成员变量sendingToBottom的值就不等于true,这也意味这个AppWindowToken对象满足条件C。

            如果能找到满足上述条件的一个AppWindowToken对象wtoken,那么我们只要找到与它所对应的Z轴位置最大的WindowState对象在WindowManagerService服务内部的WindowState堆栈中的位置i,那么将它的值加1,就可以得到与参数tokenPos所描述的AppWindowToken对象所对应的WindowState对象在WindowState堆栈的起始偏移位置了。

            那么如何找到与这个AppWindowToken对象wtoken对应的Z轴位置最大的WindowState对象在WindowManagerService服务内部的WindowState堆栈中的位置i呢?从前面的图1可以知道,一个AppWindowToken对象所对应的WindowState对象可以划分为两种类型:第一种类型是父窗口类型的;第二种是子窗口类型的。如果一个WindowState对象所描述的窗口是父窗口,那么它的子窗口就保存在它的成员变量mChildWindows所描述的一个ArrayList中,并且这些子窗口是按照Z轴位置从小到大的顺序排列的,同时,该WindowState对象也会保存在与它所对应的一个AppWindowToken对象的成员变量windows所描述的一个ArrayList中。

            有了上述结论,并且假设存在一个能够满足上述三个条件的AppWindowToken对象wtoken,那么就可以从上到下遍历保存在它的成员变量windows所描述的一个ArrayList中的每一个WindowState对象win:

            I. 如果WindowState对象win所描述的一个窗口具有子窗口,那么就继续从上到下遍历这些子窗口,即从上到下遍历WindowState对象win的成员变量mChildWindows所描述的一个ArrayList。如果能找到一个WindowState对象cwin,它的成员变量mSubLayer的值大于等于0,那么该WindowState对象cwin在WindowManagerService服务内部的WindowState堆栈中的位置就是我们要得到的位置i。注意,如果WindowState对象cwin的成员变量mSubLayer的值小于0,那么它虽然是一个子窗口,但是它却是位于父窗口的后面的,即它的Z轴位置是小于父窗口的Z轴位置的。

            II. 如果WindowState对象win所描述的一个窗口不具有子窗口,即它的成员变量mChildWindows所描述的一个ArrayList的大小等于0,那么它在WindowManagerService服务内部的WindowState堆栈中的位置就是我们要得到的位置i。

            得到了位置i之后,将它的值加1,那么就可以得到与参数tokenPos所描述的AppWindowToken对象所对应的WindowState对象在WindowState堆栈的起始偏移位置了。

            回到WindowManagerService类的成员函数moveAppToken中,调整好参数token所描述的AppWindowToken对象所对应的WindowState对象在WindowState堆栈中的位置之后,即调用了成员函数reAddAppWindowsLocked之后,这时候系统中的窗口的布局就会发生了变化,即系统中的窗口的Z轴位置关系发生了变化,那么接下来就需要调用成员函数updateFocusedWindowLocked来重新计算系统中的窗口的Z轴位置,并且调用成员函数performLayoutAndPlaceSurfacesLocked来重新布局系统中的窗口。

            4. 移动AppWindowToken至顶端

            移动AppWindowToken至顶端是通过调用WindowManagerService类的成员函数moveAppTokensToTop来实现的,如下所示:

    public class WindowManagerService extends IWindowManager.Stub  
            implements Watchdog.Monitor {  
        ......  
      
        public void moveAppTokensToTop(List<IBinder> tokens) {
            if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                    "moveAppTokensToTop()")) {
                throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
            }
    
            final long origId = Binder.clearCallingIdentity();
            synchronized(mWindowMap) {
                removeAppTokensLocked(tokens);
                final int N = tokens.size();
                for (int i=0; i<N; i++) {
                    AppWindowToken wt = findAppWindowToken(tokens.get(i));
                    if (wt != null) {
                        mAppTokens.add(wt);
                        if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
                            mToTopApps.remove(wt);
                            mToBottomApps.remove(wt);
                            mToTopApps.add(wt);
                            wt.sendingToBottom = false;
                            wt.sendingToTop = true;
                        }
                    }
                }
    
                if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET) {
                    moveAppWindowsLocked(tokens, mAppTokens.size());
                }
            }
            Binder.restoreCallingIdentity(origId);
        }
     
        ......  
    }  
            这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

            WindowManagerService类的成员函数moveAppTokensToTop可以同时将一组AppWindowToken移至顶端,同时需要调用者具有android.Manifest.permission.MANAGE_APP_TOKENS权限。

            参数tokens所描述的是一个IBinder接口列表,与这些IBinder接口所对应的AppWindowToken对象就是接下来要移至顶端的。在将保存在参数tokens中的IBinder接口所对应的AppWindowToken对象移至顶端之前,WindowManagerService类的成员函数首先会调用前面所描述的成员函数removeAppTokensLocked来删除这些AppWindowToken对象,然后再依次将它们添加到WindowManagerService类的成员变量mAppTokens所描述的一个ArrayList的末尾去。

            注意,WindowManagerService类的成员变量mNextAppTransition用来描述系统当前是否正在切换Activity窗口。如果是的话,那么它的值就不等于WindowManagerPolicy.TRANSIT_UNSET,这时候就需要:

           A. 将所有要移至顶端的AppWindowToken对象都保存在WindowManagerService类的另外一个成员变量mToTopApps所描述的一个ArrayList中去,并且将这些AppWindowToken对象的成员变量sendingToTop的值设置为true。

           B. 将所有要移至顶端的AppWindowToken对象所对应WindowState对象都移至WindowManagerService服务内部的一个WindowState堆栈的顶端去,这是通过调用另外一个成员函数moveAppWindowsLocked来实现的。

           执行完成上述两个操作之后,与要移至顶端的AppWindowToken对象所对应的窗口就会位于窗口堆栈的最上面了。

           5. 移动AppWindowToken至底端

           移动AppWindowToken至顶端是通过调用WindowManagerService类的成员函数moveAppTokensToBottom来实现的,如下所示:

    public class WindowManagerService extends IWindowManager.Stub  
            implements Watchdog.Monitor {  
        ......  
      
        public void moveAppTokensToBottom(List<IBinder> tokens) {
            if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                    "moveAppTokensToBottom()")) {
                throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
            }
    
            final long origId = Binder.clearCallingIdentity();
            synchronized(mWindowMap) {
                removeAppTokensLocked(tokens);
                final int N = tokens.size();
                int pos = 0;
                for (int i=0; i<N; i++) {
                    AppWindowToken wt = findAppWindowToken(tokens.get(i));
                    if (wt != null) {
                        mAppTokens.add(pos, wt);
                        if (mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
                            mToTopApps.remove(wt);
                            mToBottomApps.remove(wt);
                            mToBottomApps.add(i, wt);
                            wt.sendingToTop = false;
                            wt.sendingToBottom = true;
                        }
                        pos++;
                    }
                }
    
                if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET) {
                    moveAppWindowsLocked(tokens, 0);
                }
            }
            Binder.restoreCallingIdentity(origId);
        }
     
        ......  
    }  
            这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

            WindowManagerService类的成员函数moveAppTokensToBottom可以同时将一组AppWindowToken移至底端。将一组AppWindowToken移至底端与将一组AppWindowToken移至顶端的实现是类似的,只不过是移动的方向相反而已。因此,WindowManagerService类的成员函数moveAppTokensToBottom的实现可以参考前面所分析的成员函数moveAppTokensToTop的实现,这里不再详述。

            6. 增加WindowToken

            从图1可以知道,如果一个WindowState对象不是与一个AppWindowToken对象对应的,那么它就必须要与一个WindowToken对象对应。例如,用来描述输入法窗口和壁纸窗口的WindowState对象对应的就是WindowToken对象,而不是AppWindowToken对象,因为它们不是Activity类型的窗口。

            输入法窗口和壁纸窗口分别是由输入法管理服务InputMethodManagerService和壁纸管理服务WallpaperManagerService调用WindowManagerService类的成员函数addWindowToken来增加对应的WindowToken对象的,如下所示:

    public class WindowManagerService extends IWindowManager.Stub  
            implements Watchdog.Monitor {  
        ......  
      
        public void addWindowToken(IBinder token, int type) {
            if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                    "addWindowToken()")) {
                throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
            }
    
            synchronized(mWindowMap) {
                WindowToken wtoken = mTokenMap.get(token);
                if (wtoken != null) {
                    Slog.w(TAG, "Attempted to add existing input method token: " + token);
                    return;
                }
                wtoken = new WindowToken(token, type, true);
                mTokenMap.put(token, wtoken);
                mTokenList.add(wtoken);
                if (type == TYPE_WALLPAPER) {
                    mWallpaperTokens.add(wtoken);
                }
            }
        }
     
        ......  
    }
            这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

            调用WindowManagerService类的成员函数addWindowToken需要具有android.Manifest.permission.MANAGE_APP_TOKENS权限。

            对于输入法窗口和壁纸窗口来说,参数token指向的是与它们所关联的一个Binder对象的IBinder接口,而参数type描述的是要在WindowManagerService服务内部增加WindowToken对象的窗口的类型。

            WindowManagerService类的成员函数addWindowToken首先检查在成员变量mTokenMap所描述的一个HashMap检查是否已经存在一个WindowToken对象与参数token对应。如果已经存在的话,那么WindowManagerService类的成员函数addWindowToken就什么也不做就返回了,否则的话,就会使用参数token来创建一个WindowToken对象,并且会将该WindowToken对象分别保存在WindowManagerService类的成员变量mTokenMap和mTokenList中。

            这里有两个地方需要注意:

            A. 由于这里增加的是WindowToken对象,而不是AppWindowToken对象,因此,与增加AppWindowToken不同,这里不需要将新创建的WindowToken对象保存在WindowManagerService类的成员变量mAppTokens中。

            B. 如果参数type的值等于TYPE_WALLPAPER,那么就意味着新创建的WindowToken对象是用来描述壁纸窗口的,这时候还需要将新创建的WindowToken对象保存在WindowManagerService类的成员变量mWallpaperTokens所描述的一个ArrayList中,以方便管理壁纸窗口。

            对于非输入法窗口、非壁纸窗口以及非Activity窗口来说,它们所对应的WindowToken对象是在它们增加到WindowManagerService服务的时候创建的。从前面Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析一文可以知道,增加一个窗口WindowManagerService服务最终是通过调用WindowManagerService类的成员函数addWindow来实现的,接下来我们就主要分析与创建WindowToken相关的逻辑,如下所示:

    public class WindowManagerService extends IWindowManager.Stub  
            implements Watchdog.Monitor {  
        ......  
      
        public int addWindow(Session session, IWindow client,
                WindowManager.LayoutParams attrs, int viewVisibility,
                Rect outContentInsets, InputChannel outInputChannel) {
            ......
    
            synchronized(mWindowMap) {
                ......
    
                boolean addToken = false;
                WindowToken token = mTokenMap.get(attrs.token);
                if (token == null) {
                    if (attrs.type >= FIRST_APPLICATION_WINDOW
                            && attrs.type <= LAST_APPLICATION_WINDOW) {
                        ......
                        return WindowManagerImpl.ADD_BAD_APP_TOKEN;
                    }
                    if (attrs.type == TYPE_INPUT_METHOD) {
                        ......
                        return WindowManagerImpl.ADD_BAD_APP_TOKEN;
                    }
                    if (attrs.type == TYPE_WALLPAPER) {
                        ......
                        return WindowManagerImpl.ADD_BAD_APP_TOKEN;
                    }
                    token = new WindowToken(attrs.token, -1, false);
                    addToken = true;
                }
    
                ......
    
                if (addToken) {
                    mTokenMap.put(attrs.token, token);
                    mTokenList.add(token);
                }
     
                ......
            }
    
            ......
        }
             
        ......  
    }
            这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

            如果参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量token所指向的一个IBinder接口在WindowManagerService类的成员变量mTokenMap所描述的一个HashMap中没有一个对应的WindowToken对象,并且该WindowManager.LayoutParams对象的成员变量type的值不等于TYPE_INPUT_METHOD、TYPE_WALLPAPER,以及不在FIRST_APPLICATION_WINDOW和LAST_APPLICATION_WINDOW,那么就意味着这时候要增加的窗口就既不是输入法窗口,也不是壁纸窗口和Activity窗口,因此,就需要以参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量token所指向的一个IBinder接口为参数来创建一个WindowToken对象,并且将该WindowToken对象保存在WindowManagerService类的成员变量mTokenMap和mTokenList中。

            7. 删除WindowToken

            删除WindowToken是通过调用WindowManagerService类的成员函数removeWindowToken来实现的,如下所示:

    public class WindowManagerService extends IWindowManager.Stub  
            implements Watchdog.Monitor {  
        ......  
      
        public void removeWindowToken(IBinder token) {
            if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                    "removeWindowToken()")) {
                throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
            }
    
            final long origId = Binder.clearCallingIdentity();
            synchronized(mWindowMap) {
                WindowToken wtoken = mTokenMap.remove(token);
                mTokenList.remove(wtoken);
                if (wtoken != null) {
                    boolean delayed = false;
                    if (!wtoken.hidden) {
                        wtoken.hidden = true;
    
                        final int N = wtoken.windows.size();
                        boolean changed = false;
    
                        for (int i=0; i<N; i++) {
                            WindowState win = wtoken.windows.get(i);
    
                            if (win.isAnimating()) {
                                delayed = true;
                            }
    
                            if (win.isVisibleNow()) {
                                applyAnimationLocked(win,
                                        WindowManagerPolicy.TRANSIT_EXIT, false);
                                changed = true;
                            }
                        }
    
                        if (changed) {
                            mLayoutNeeded = true;
                            performLayoutAndPlaceSurfacesLocked();
                            updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL);
                        }
    
                        if (delayed) {
                            mExitingTokens.add(wtoken);
                        } else if (wtoken.windowType == TYPE_WALLPAPER) {
                            mWallpaperTokens.remove(wtoken);
                        }
                    }
    
                    ......
                } else {
                    Slog.w(TAG, "Attempted to remove non-existing token: " + token);
                }
            }
            Binder.restoreCallingIdentity(origId);
        }
             
        ......  
    }
            这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

            调用WindowManagerService类的成员函数removeWindowToken需要具有android.Manifest.permission.MANAGE_APP_TOKENS权限。

            WindowManagerService类的成员函数removeWindowToken首先找到与参数token所描述的Binder接口所对应的WindowToken对象,接着再将该WindowToken对象从WindowManagerService类的成员变量mTokenMap和mTokenList中删除。

            删除了一个WindowToken对象之后,如果该WindowToken对象不是处于不可见的状态,即它的成员变量hidden的值不等于false,那么就意味着它所描述窗口口也有可能是可见的,那么WindowManagerService类的成员函数removeWindowToken就需要作以下两个检查:

            A. 如果该WindowToken对象所描述的窗口的其中一个处于动画显示过程,即用来描述该窗口的一个WindowState对象的成员函数isAnimating的返回值等于true,那么就需要该WindowToken对象的状态设置为正在退出状态,即将它保存在WindowManagerService类的成员变量mExitingTokens所描述的一个ArrayList中。

            B. 如果该WindowToken对象所描述的窗口是可见的,即用来描述该窗口的一个WindowState对象的成员函数isVisibleNow的返回值等于true,那么就需要调用WindowManagerService类的成员函数applyAnimationLocked来给它应用一个退出动画,该退出动画是通过调用WindowManagerService类的成员函数performLayoutAndPlaceSurfacesLocked来实现的。当一个窗口退出了之后,系统当前获得焦点的窗口可能会发生变化,这时候就需要调用WindowManagerService类的成员函数updateFocusedWindowLocked来重新调整系统当前获得焦点的窗口。

            注意,如果正在删除的WindowToken对象是用来描述壁纸窗口的,那么还需要将该WindowToken对象从WindowManagerService类的成员变量mWallpaperTokens所描述的一个ArrayList中删除。

            8. 增加WindowState

            从前面Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析一文可以知道,增加一个窗口WindowManagerService服务最终是通过调用WindowManagerService类的成员函数addWindow来实现的,如下所示:

    public class WindowManagerService extends IWindowManager.Stub  
            implements Watchdog.Monitor {  
        ......  
    
        /**
         * Mapping from an IWindow IBinder to the server's Window object.
         * This is also used as the lock for all of our state.
         */
        final HashMap<IBinder, WindowState> mWindowMap = new HashMap<IBinder, WindowState>();
        ......
    
        /**
         * Z-ordered (bottom-most first) list of all Window objects.
         */
        final ArrayList<WindowState> mWindows = new ArrayList<WindowState>();
        ......
      
        public int addWindow(Session session, IWindow client,  
                WindowManager.LayoutParams attrs, int viewVisibility,  
                Rect outContentInsets, InputChannel outInputChannel) {  
            ......  
      
            WindowState win = null;  
      
            synchronized(mWindowMap) {  
                ......  
    
                win = new WindowState(session, client, token,  
                        attachedWindow, attrs, viewVisibility);  
                ......  
      
                mWindowMap.put(client.asBinder(), win);  
                ......
    
                if (attrs.type == TYPE_INPUT_METHOD) {
                    mInputMethodWindow = win;
                    addInputMethodWindowToListLocked(win);
                    ......
                } else if (attrs.type == TYPE_INPUT_METHOD_DIALOG) {
                    mInputMethodDialogs.add(win);
                    addWindowToListInOrderLocked(win, true);
                    adjustInputMethodDialogsLocked();
                    ......
                } else {
                    addWindowToListInOrderLocked(win, true);
                    if (attrs.type == TYPE_WALLPAPER) {
                        .......
                        adjustWallpaperWindowsLocked();
                    } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
                        adjustWallpaperWindowsLocked();
                    }
                }
    
                ......
            }
    
            ......
        }
    
        ......
    }
            这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

            WindowManagerService类有两个成员变量mWindowMap和mWindows是用来保存系统中的WindowState对象。其中,成员变量mWindowMap指向的是一个HashMap,它的关键字是一个IBinder接口,一般这个IBinder接口指向的是一个Binder代理对象,引用了运行在应用程序进程这一侧的一个类型为W的Binder本地对象,用来描述一个窗口;成员变量mWindows指向的是一个ArrayList,保存在它里面的WindowState对象是按照其Z轴位置从小到大的顺序排列的。成员变量mWindowMap和mWindows的区别在于,前者给在定一个IBinder接口的情况下,可以快速找到与对应的WindowState对象,而后者用来从上到下或者下到上遍历系统的WindowState对象。由于系统中的WindowState对象是按照其Z轴位置从小到大的顺序排列在成员变量mWindows中的,因此,成员变量mWindows所指向的ArrayList就是我们在前面图1中所说的Window Stack。

            理解了WindowManagerService类有两个成员变量mWindowMap和mWindows的作用之后,WindowManagerService类的成员函数addWindow增加一个WindowState对象的过程就容易理解了。

           参数client是一个Binder代理对象,引用了运行在应用程序进程这一侧的一个类型为W的Binder本地对象,用来描述要增加到WindowManagerService服务中的一个窗口。WindowManagerService类的成员函数addWindow首先创建一个WindowState对象win,接着再以参数client所描述的一个Binder代理对象的IBinder接口为关键字,将WindowState对象win保存在WindowManagerService类的成员变量mWindowMap中,最后还会根据要增加到WindowManagerService服务中的窗口的类型来调用不同的成员函数将WindowState对象win增加到WindowManagerService类的成员变量mWindows中:

            A. 如果要增加的是输入法窗口,即参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量type的值等于TYPE_INPUT_METHOD,那么就会调用成员函数addInputMethodWindowToListLocked来将WindowState对象win增加到WindowManagerService类的成员变量mWindows中去,并且会将WindowState对象win保存在WindowManagerService类的成员变量mInputMethodWindow中。

            B. 如果要增加的是输入法对话框,即参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量type的值等于TYPE_INPUT_METHOD_DIALOG,那么就会调用成员函数addWindowToListInOrderLocked来将WindowState对象win增加到WindowManagerService类的成员变量mWindows中去,并且会将WindowState对象win保存在WindowManagerService类的成员变量mInputMethodDialogs中,以及调用成员函数adjustInputMethodDialogsLocked来调整刚才所添加的输入法窗口在窗口堆栈中的位置,使得它位于系统当前需要输入法窗口的窗口的上面。

            C.  如果要增加的是壁纸窗口,即参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量type的值等于TYPE_WALLPAPER,那么就会调用成员函数addWindowToListInOrderLocked来将WindowState对象win增加到WindowManagerService类的成员变量mWindows中去,并且会调用成员函数adjustWallpaperWindowsLocked来调整刚才所添加的壁纸窗口在窗口堆栈中的位置,使得它位于系统当前需要壁纸窗口的窗口的下面。

            D . 如果要增加的既不是输入法窗口,也不是输入法对话框和壁纸窗口,那么就只会调用成员函数addWindowToListInOrderLocked来将WindowState对象win增加到WindowManagerService类的成员变量mWindows中去,但是如果要增加的窗口需要显示壁纸,即参数attrs所描述的一个WindowManager.LayoutParams对象的成员变量flags的FLAG_SHOW_WALLPAPER位等于1,那么还会继续调用成员函数adjustWallpaperWindowsLocked来调整系统中的壁纸窗口在窗口堆栈中的位置,使得它位于刚才所添加的窗口的下面。

            在后面的两篇文章中,我们再详细分析WindowManagerService类的成员函数addInputMethodWindowToListLocked、adjustInputMethodDialogsLocked和adjustWallpaperWindowsLocked的实现,其中,前两者是与输入法窗口相关的,而后者是与壁纸窗口相关的。本文主要关注WindowManagerService类的成员函数addWindowToListInOrderLocked的实现,它会将一个指定的WindowState对象增加到窗口堆栈中的合适位置上去。

            9. 增加WindowState到窗口堆栈

            从前面的分析可以知道,将一个WindowState对象增加到WindowManagerService服务内部中的窗口堆栈,即WindowManagerService类的成员变量mWindows,是通过调用WindowManagerService类的成员函数addWindowToListInOrderLocked来实现的。

           WindowManagerService类的成员函数addWindowToListInOrderLocked的实现比较复杂,我们先列出它的框架,然后再详细分析它的实现,如下所示:

    public class WindowManagerService extends IWindowManager.Stub  
            implements Watchdog.Monitor {  
        ......  
    
        private void addWindowToListInOrderLocked(WindowState win, boolean addToToken) {
            final IWindow client = win.mClient;
            final WindowToken token = win.mToken;
            final ArrayList<WindowState> localmWindows = mWindows;
    
            final int N = localmWindows.size();
            final WindowState attached = win.mAttachedWindow;
            int i;
            if (attached == null) {
                //CASE 1:要增加的窗口win没有附加在其它窗口上
                int tokenWindowsPos = token.windows.size();
                if (token.appWindowToken != null) {
                    //CASE 1.1:要增加的窗口win是一个Activity窗口
                    int index = tokenWindowsPos-1;
                    if (index >= 0) {
                        //CASE 1.1.1:用来要增加的窗口win的令牌token已存在其它窗口
                        ......
                    } else {
                        //CASE 1.1.2:用来要增加的窗口win的令牌token尚未存在任何窗口
                        ......
                    }
                } else {
                    //CASE 1.2:要增加的窗口win不是一个Activity窗口
                    ......
                }
                if (addToToken) {
                    token.windows.add(tokenWindowsPos, win);
                }
            } else {
                //CASE 2:要增加的窗口win附加在窗口attached上
                ......
            }
    
            if (win.mAppToken != null && addToToken) {
                win.mAppToken.allAppWindows.add(win);
            }
        }
    
        ......
    }
            这个函数定义在文件frameworks/base/services/java/com/android/server/WindowManagerService.java中。

            我们首先分析一下WindowManagerService类的成员函数addWindowToListInOrderLocked的几个本地变量的含义:

            A. token。本地变量token指向的是参数win所描述的一个WindowState对象的成员变量mToken所指向一个WindowToken对象,这个WindowToken对象用来描述WindowState对象win所对应的窗口令牌。

            B. localmWindows。本地变量localmWindows指向的是WindowManagerService类的成员变量mWindows所描述的一个ArrayList,即一个窗口堆栈,WindowManagerService类的成员函数addWindowToListInOrderLocked的目标就是要将参数win所描述的一个WindowState对象增加到该窗口堆栈的合适位置上去。

            C. attached。本地变量attached指向的是参数win所描述的一个WindowState对象的成员变量mAttachedWindow 所指向的一个WindowState对象,如果它的值不等于null,那么就意味参数win所描述的窗口要附加在本地变量attached所描述的窗口上。

            D. tokenWindowsPos。本地变量tokenWindowsPos用来描述与窗口令牌token所对应的窗口的数量。

            E. token.appWindowToken。从前面Android应用程序窗口(Activity)与WindowManagerService服务的连接过程分析一文可以知道,如果一个WindowToken对象的成员变量appWindowToken的值不等于null,那么就意味着该WindowToken对象的实际类型为是AppWindowToken,即它所描述的是一个Activity窗口令牌,这种类型的令牌的特点是在ActivityManagerService服务的Activity组件堆栈中对应有一个ActivityRecord对象,如图1所示。

            F. index。本地变量index的值等于tokenWindowsPos-1,如果它的值大于等于0,那么就意味着窗口令牌tokent已经存在其它窗口,否则的话,就意味着窗口令牌tokent尚未存在任何窗口。

            从这些本地变量的含义,我们就可以分情况来将参数win所描述的一个WindowState对象增加到WindowManagerService服务内部的窗口堆栈的合适位置上去:

            CASE 1:要增加的窗口win没有附加在其它窗口上

            ----CASE 1.1:要增加的窗口win是一个Activity窗口

                 ----CASE 1.1.1:用来要增加的窗口win的令牌token已存在其它窗口。这时候意味着窗口win需要保存在其它已经存在的窗口的附近,因此,我们只要找到这些已经存在的窗口在窗口堆栈中的位置,那么再根据其它属性,就可以将窗口win保存在已经存在的窗口的上面或者下面。

                 ----CASE 1.1.2:用来要增加的窗口win的令牌token尚未存在任何窗口。虽然这时候窗口win在窗口堆栈中没有位置可以参考,但是它毕竟是一个Activity窗口,我们可以通过与它所对应的AppWindowToken对象在App Token List(即WindowManagerService类的成员变量mAppTokens所描述的一个ArrayList)中的位置来获得它窗口堆栈中的位置。回忆我们在前面第3节分析移动AppWindowToken至指定位置的操作时得到的结论:WindowManagerService服务内部中的所有WindowState对象都是按照Z轴从位置从小到大排列在WindowState堆栈中的,并且在mAppTokens列表中,位于上面的一个AppWindowToken对象所对应的那些WindowState对象的Z轴位置是一定大于位于下面的一个AppWindowToken对象所对应的那些WindowState对象的Z轴位置的。因此,我们只要找到用来描述窗口win的一个AppWindowToken对象(token.appWindowToken)的上一个或者下一个AppWindowToken对象所对应的窗口在窗口堆栈中的位置,那么就可以这个位置为参考,得到窗口win在窗口堆栈中的位置。

            ----CASE 1.2:要增加的窗口win不是一个Activity窗口。这时候既然要增加的窗口也没有附加在其它窗口上,那么就意味着要增加的窗口win在窗口堆栈中没有位置可以参考,因此,我们就需要根据它的Z轴位置来决定它在窗口堆栈的位置。

            CASE 2:要增加的窗口win附加在窗口attached上。这时候就意味着要增加的窗口win要保存在窗口attached的上面,即窗口在窗口堆栈的位置要以窗口attached在窗口堆栈的位置为参考。

            从上面的分析就可以知道,CASE 1.1.1CASE 1.1.2CASE 2都有一个共同特点,即要增加的窗口win在窗口堆栈的位置有一个参考值,而在CASE 1.2中,要增加的窗口win在窗口堆栈的位置没有参考值,需要通过其Z轴位置来确定。

           在分析上述四种情况之前, 我们还需要再说明一下WindowManagerService类的成员函数addWindowToListInOrderLocked的参数addToToken的含义。参数addToToken是一个布尔变量,如果它的值等于true,那么就说明需要将参数win所描述的一个WindowState对象添加用来描述它的窗口令牌token的成员变量windows所描述的一个ArrayList中去。注意,窗口令牌token的成员变量windows所描述的一个ArrayList里面所保存的WindowState对象是按照Z轴位置从小到大的顺序来排列的,因此,在将WindowState对象win保存到这个ArrayList之前,首先要按照它的Z轴位置计算得到它在这个ArrayList中的位置tokenWindowsPos。另一方面,在参数addToToken的值等于true,并且参数win所描述的是一个Activity窗口,即它的成员变量mAppToken不等于null的情况下,还需要将参数win所描述的一个WindowState对象保存在用来描述它的窗口令牌,即一个AppWindowToken对象成员变量allAppWindows所描述的一个ArrayList中去,以便可以知道一个AppWindowToken对象对应的Activity窗口都有哪些。

           接下来,我们就分别分析这四种情况是如何将窗口win增加窗口堆栈中去的。

           CASE 1.1.1对应的代码为:

                        if (win.mAttrs.type == TYPE_BASE_APPLICATION) {
                            // Base windows go behind everything else.
                            placeWindowBefore(token.windows.get(0), win);
                            tokenWindowsPos = 0;
                        } else {
                            AppWindowToken atoken = win.mAppToken;
                            if (atoken != null &&
                                    token.windows.get(index) == atoken.startingWindow) {
                                placeWindowBefore(token.windows.get(index), win);
                                tokenWindowsPos--;
                            } else {
                                int newIdx =  findIdxBasedOnAppTokens(win);
                                if(newIdx != -1) {
                                    //there is a window above this one associated with the same
                                    //apptoken note that the window could be a floating window
                                    //that was created later or a window at the top of the list of
                                    //windows associated with this token.
                                    ......
                                    localmWindows.add(newIdx+1, win);
                                    mWindowsChanged = true;
                                }
                            }
                        }

            这段代码又分为三种情况来将参数win所描述的一个WindowState对象添加到窗口堆栈中:

            A. 参数win描述的窗口的类型为TYPE_BASE_APPLICATION。在一个令牌对应的所有窗口中,类型为TYPE_BASE_APPLICATION的窗口位于其它类型的窗口的下面。因此,这段代码就会调用WindowManagerService类的成员函数placeWindowBefore来将参数win所描述的一个WindowState对象保存窗口堆栈中,并且它是位于令牌token的窗口列表的第0个位置的WindowState对象的下面。这时候变量tokenWindowsPos的值会被设置为0,表示参数win所描述的一个WindowState对象要保存窗口令牌token的窗口列表的第0个位置上。

            B. 参数win描述的一个WindowState对象的成员变量mAppToken的值不等于null,这意味着参数win描述的是一个Activity窗口,这时候如果窗口令牌atoken(与token描述的是同一个窗口令牌)的窗口列表的第index个位置(即最上面的一个位置) 的WindowState对象描述的是一个Activity启动窗口,即与窗口令牌atoken的成员变量startingWindow描述的是同一个窗口,那么就说明窗口令牌atoken的窗口列表的第index个位置的WindowState对象描述的是窗口win的启动窗口。由于一个窗口的启动窗口总是位于它的上面,因此,这段代码就会调用WindowManagerService类的成员函数placeWindowBefore来将参数win所描述的一个WindowState对象保存窗口堆栈中,并且它是位于令牌atoken的窗口列表的第index个位置的WindowState对象的下面。这时候变量tokenWindowsPos的值减少1,即相当于是等于index,表示参数win所描述的一个WindowState对象要插入在窗口令牌token的窗口列表的第index个位置上。

            C. 参数win所描述的窗口的类型既不是TYPE_BASE_APPLICATION,而且它也没有启动窗口,那么这时候就需要将它保存在窗口令牌token的窗口列表的最上面一个窗口的上面。窗口令牌token的窗口列表的最上面一个窗口在窗口堆栈中的位置newIdx是通过调用WindowManagerService类的成员函数findIdxBaseOnAppTokens来获得的,这时候参数win所描述的一个WindowState对象就应该保存在窗口堆栈,即变量localmWindows所描述的一个ArrayList的第newIdx+1个位置上。

            CASE 1.1.2对应的代码为:

                        // Figure out where the window should go, based on the
                        // order of applications.
                        final int NA = mAppTokens.size();
                        WindowState pos = null;
                        for (i=NA-1; i>=0; i--) {
                            AppWindowToken t = mAppTokens.get(i);
                            if (t == token) {
                                i--;
                                break;
                            }
    
                            // We haven't reached the token yet; if this token
                            // is not going to the bottom and has windows, we can
                            // use it as an anchor for when we do reach the token.
                            if (!t.sendingToBottom && t.windows.size() > 0) {
                                pos = t.windows.get(0);
                            }
                        }
                        // We now know the index into the apps.  If we found
                        // an app window above, that gives us the position; else
                        // we need to look some more.
                        if (pos != null) {
                            // Move behind any windows attached to this one.
                            WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
                            if (atoken != null) {
                                final int NC = atoken.windows.size();
                                if (NC > 0) {
                                    WindowState bottom = atoken.windows.get(0);
                                    if (bottom.mSubLayer < 0) {
                                        pos = bottom;
                                    }
                                }
                            }
                            placeWindowBefore(pos, win);
                        } else {
                            // Continue looking down until we find the first
                            // token that has windows.
                            while (i >= 0) {
                                AppWindowToken t = mAppTokens.get(i);
                                final int NW = t.windows.size();
                                if (NW > 0) {
                                    pos = t.windows.get(NW-1);
                                    break;
                                }
                                i--;
                            }
                            if (pos != null) {
                                // Move in front of any windows attached to this
                                // one.
                                WindowToken atoken = mTokenMap.get(pos.mClient.asBinder());
                                if (atoken != null) {
                                    final int NC = atoken.windows.size();
                                    if (NC > 0) {
                                        WindowState top = atoken.windows.get(NC-1);
                                        if (top.mSubLayer >= 0) {
                                            pos = top;
                                        }
                                    }
                                }
                                placeWindowAfter(pos, win);
                                placeWindowAfter(pos, win);
                            } else {
                                // Just search for the start of this layer.
                                final int myLayer = win.mBaseLayer;
                                for (i=0; i<N; i++) {
                                    WindowState w = localmWindows.get(i);
                                    if (w.mBaseLayer > myLayer) {
                                        break;
                                    }
                                }
                                ......
                                localmWindows.add(i, win);
                                mWindowsChanged = true;
                            }
                        }
            这段代码要能冠军WindowManagerService服务内部的一个AppWindowToken列表mAppTokens来在窗口堆栈中找到一个参数位置来保存参数win所描述的一个WindowState对象。

            最上面的一个for循环执行完成之后,我们假设变量pos的值不等于null,这时候它与变量i以及变量token的关系如图2所示:


    图2 窗口win位于窗口C的下面

            这时候位于令牌token上面的令牌在窗口堆栈中对应有WindowState对象。注意,这时候第i+2个令牌在窗口堆栈中不对应有WindowState对象,而第i+3个令牌在窗口堆栈中对应有C和D两个WindowState对象,并且这两个WindowState对象所描述的窗口都不是即将要切换到窗口堆栈的底部的。由于第i+3个令牌位于令牌token的上面,并且这两个令牌之间的其它令牌在窗口堆栈中不对应有WindowState对象,因此,这时候参数win所描述的WindowState对象在窗口堆栈中的位置应该以第i+3个令牌所对应的Z轴位置最小的WindowState对象在窗口堆栈中的位置为参考,即以WindowState对象C在窗口堆栈中的位置为参考,而WindowState对象C也正好是变量pos所指向的WindowState对象。

           接下来,上述代码会继续检查WindowState对象C是否附加有SubLayer值小于0的窗口。如果有的话,那么就会将变量pos指向SubLayer值最小的那个WindowState对象,这是因为该WindowState对象是在WindowState对象C的最下面的,并且它与WindowState对象C是同属一个令牌的。最后,上述代码就会调用WindowManagerService类的成员函数placeWindowBefore来将参数win所描述的一个WindowState对象保存窗口堆栈中由变量pos所指向的那个WindowState对象的下面。

            假设最上面的一个for循环执行完成之后,变量pos的值等于null,那么就说明位于令牌token上面的令牌在窗口堆栈中都没有对应有WindowState对象,或者说它们所对应的WindowState对象都是即将要切换到窗口堆栈的底部去的,这时候就需要通过位于令牌token上面的令牌来在窗口堆栈中找到一个参考位置来保存参数win所描述的WindowState对象,这是通过中间的while循环来实现的。

           中间的while循环执行完成之后,假设变量pos的值不等于null,这时候它与变量i以及变量token的关系如图3所示:

    图3 窗口win位于窗口D的上面

            这时候位于令牌token上面的令牌在窗口堆栈中没有对应有WindowState对象。注意,这时候第i-1个令牌在窗口堆栈中不对应有WindowState对象,而第i-2个令牌在窗口堆栈中对应有C和D两个WindowState对象。由于第i-2个令牌位于令牌token的下面,并且这两个令牌之间的其它令牌在窗口堆栈中不对应有WindowState对象,因此,这时候参数win所描述的WindowState对象在窗口堆栈中的位置应该以第i-2个令牌所对应的Z轴位置最大的WindowState对象在窗口堆栈中的位置为参考,即以WindowState对象D在窗口堆栈中的位置为参考,而WindowState对象D也正好是变量pos所指向的WindowState对象。

            接下来,上述代码会继续检查WindowState对象D是否附加有SubLayer值大于等于0的窗口。如果有的话,那么就会将变量pos指向SubLayer值最大的那个WindowState对象,这是因为该WindowState对象是在WindowState对象D的最上面的,并且它与WindowState对象D是同属一个令牌的。最后,上述代码就会调用WindowManagerService类的成员函数placeWindowAfter来将参数win所描述的一个WindowState对象保存窗口堆栈中由变量pos所指向的那个WindowState对象的上面。

             假设中间的while循环执行完成之后,变量pos的值等于null,这时候就说明在窗口堆栈中实在是找不到参考位置来保存参数win所描述的WindowState对象了,因此,就只能通过参数win所描述的WindowState对象的Z轴位置,即它的成员变量mBaseLayer的值来在窗口堆栈中找到一个合适的位置了,如最下面的for循环所示。由于窗口堆栈中的WindowState对象是按照它们的Z轴位置由小到大的顺序来排列的,因此,最下面的for循环只要从下到上找到一个Z轴位置比参数win所描述的WindowState对象的Z轴位置大的一个WindowState对象在窗口堆栈中的位置i,那么就可以将参数win所描述的WindowState对象插入在窗口堆栈的第i个位置上了。

             CASE 1.2对应的代码为:

                    // Figure out where window should go, based on layer.
                    final int myLayer = win.mBaseLayer;
                    for (i=N-1; i>=0; i--) {
                        if (localmWindows.get(i).mBaseLayer <= myLayer) {
                            i++;
                            break;
                        }
                    }
                    if (i < 0) i = 0;
                    ......
                    localmWindows.add(i, win);
                    mWindowsChanged = true;
            由于这时候在窗口堆栈中是没有参考位置来保存参数win所描述的WindowState对象的,因此,这段代码就只能通过参数win所描述的WindowState对象的Z轴位置,即它的成员变量mBaseLayer的值来在窗口堆栈中找到一个合适的位置了,如这段代码中的for循环所示。由于窗口堆栈中的WindowState对象是按照它们的Z轴位置由小到大的顺序来排列的,因此,这段代码中的for循环只要从上到下找到一个WindowState对象,它的Z轴位置小于或者等于参数win所描述的WindowState对象的Z轴位置,那么该WindowState对象在窗口堆栈中的位置i就可以用插入参数win所描述的WindowState对象了。

            CASE 2对应的代码为:

                // Figure out this window's ordering relative to the window
                // it is attached to.
                final int NA = token.windows.size();
                final int sublayer = win.mSubLayer;
                int largestSublayer = Integer.MIN_VALUE;
                WindowState windowWithLargestSublayer = null;
                for (i=0; i<NA; i++) {
                    WindowState w = token.windows.get(i);
                    final int wSublayer = w.mSubLayer;
                    if (wSublayer >= largestSublayer) {
                        largestSublayer = wSublayer;
                        windowWithLargestSublayer = w;
                    }
                    if (sublayer < 0) {
                        // For negative sublayers, we go below all windows
                        // in the same sublayer.
                        if (wSublayer >= sublayer) {
                            if (addToToken) {
                                token.windows.add(i, win);
                            }
                            placeWindowBefore(
                                wSublayer >= 0 ? attached : w, win);
                            break;
                        }
                    } else {
                        // For positive sublayers, we go above all windows
                        // in the same sublayer.
                        if (wSublayer > sublayer) {
                            if (addToToken) {
                                token.windows.add(i, win);
                            }
                            placeWindowBefore(w, win);
                            break;
                        }
                    }
                }
                if (i >= NA) {
                    if (addToToken) {
                        token.windows.add(win);
                    }
                    if (sublayer < 0) {
                        placeWindowBefore(attached, win);
                    } else {
                        placeWindowAfter(largestSublayer >= 0
                                         ? windowWithLargestSublayer
                                         : attached,
                                         win);
                    }
                }
            这段代码要将参数win所描述的WindowState对象附加在变量attached所描述的WindowState对象的上面或者下面,取决于它的成员变量mSubLayer的值是大于0还是小于0。我们分四种情况来考虑。

            第一种情况是参数win所描述的WindowState对象的成员变量mSubLayer的值小于0,并且这时候在附加在窗口attached的WindowState对象中,存在一个WindowState对象,它的成员变量mSubLayer的值大于等于参数win所描述的WindowState对象的成员变量mSubLayer的值,如图4和图5所示:


    图4 窗口win插入到窗口B的下面


    图5 窗口win插入在窗口attached的下面

            在图4和图5中,WindowState对象A和B均是附加在WindowState对象attached中。

            在图4中,WindowState对象A和B的成员变量mSubLayer的值均小于0,而WindowState对象win的成员变量mSubLayer的值比WindowState对象A的大,但是比WindowState对象B的小,这时候WindowState对象win在窗口堆栈中就应该位于WindowState对象B的下面,这是通过调用WindowManagerService类的成员函数placeWindowBefore来实现的。

            在图5中,WindowState对象A和B的成员变量mSubLayer的值均大于0,由于WindowState对象win的成员变量mSubLayer的值小于0,这时候WindowState对象win在窗口堆栈中就应该位于WindowState对象attached的下面,这是通过调用WindowManagerService类的成员函数placeWindowBefore来实现的。

            第二种情况是参数win所描述的WindowState对象的成员变量mSubLayer的值大于0,并且这时候在附加在窗口attached的WindowState对象中,存在一个WindowState对象,它的成员变量mSubLayer的值大于参数win所描述的WindowState对象的成员变量mSubLayer的值,如图6所示:


    图6 窗口win插入在窗口B的下面

            在图6中,WindowState对象A和B均是附加在WindowState对象attached中。其中,WindowState对象A和B的成员变量mSubLayer的值均大于0,而WindowState对象win的成员变量mSubLayer的值比WindowState对象A的大,但是比WindowState对象B的小,这时候WindowState对象win在窗口堆栈中就应该位于WindowState对象B的下面,这是通过调用WindowManagerService类的成员函数placeWindowBefore来实现的。

            第三种情况是参数win所描述的WindowState对象的成员变量mSubLayer的值小于0,但是在附加在窗口attached的WindowState对象中,找不到一个WindowState对象,它的成员变量mSubLayer的值比WindowState对象的成员变量mSubLayer的值大,如图7所示:


    图7 窗口win插入在窗口attached的下面

            在图7中,WindowState对象A和B均是附加在WindowState对象attached中。其中,WindowState对象A和B以及win的成员变量mSubLayer的值均小于0,但是WindowState对象win的成员变量mSubLayer的值比WindowState对象A和B的都要大,这时候WindowState对象win在窗口堆栈中就应该位于WindowState对象attached的下面,这是通过调用WindowManagerService类的成员函数placeWindowBefore来实现的。

            第四种情况是参数win所描述的WindowState对象的成员变量mSubLayer的值大于等于0,但是在附加在窗口attached的WindowState对象中,找不到一个WindowState对象,它的成员变量mSubLayer的值比WindowState对象的成员变量mSubLayer的值大,如图8和图9所示:


    图8 窗口win插入在窗口B的上面


    图9 窗口win插入在窗口attached的上面

            在图8和图9中,WindowState对象A和B均是附加在WindowState对象attached中。

            在图8中,WindowState对象A和B的成员变量mSubLayer的值均大于0,并且WindowState对象win的成员变量mSubLayer的值比WindowState对象A和B的都要大,这时候WindowState对象win在窗口堆栈中就应该位于WindowState对象B的上面,这是通过调用WindowManagerService类的成员函数placeWindowAfter来实现的。

            在图9中,WindowState对象A和B的成员变量mSubLayer的值均小于等于0,而WindowState对象win的成员变量mSubLayer的值大于0,这时候WindowState对象win在窗口堆栈中就应该位于WindowState对象attached的上面,这是通过调用WindowManagerService类的成员函数placeWindowAfter来实现的。

             注意,在这四种情况中,如果参数addToToken的值等于true,那么都需要将参数win所描述的WindowState对象增加到与它所对应的窗口令牌token的窗口列表windows中去。

             10. 删除WindowState

              删除WindowState是通过调用WindowManagerService类的成员函数tmpRemoveWindowLocked来实现的,如下所示:

    public class WindowManagerService extends IWindowManager.Stub  
            implements Watchdog.Monitor {  
        ......  
      
        private int tmpRemoveWindowLocked(int interestingPos, WindowState win) {
            int wpos = mWindows.indexOf(win);
            if (wpos >= 0) {
                if (wpos < interestingPos) interestingPos--;
                ......
                mWindows.remove(wpos);
                mWindowsChanged = true;
                int NC = win.mChildWindows.size();
                while (NC > 0) {
                    NC--;
                    WindowState cw = win.mChildWindows.get(NC);
                    int cpos = mWindows.indexOf(cw);
                    if (cpos >= 0) {
                        if (cpos < interestingPos) interestingPos--;
                        ......
                        mWindows.remove(cpos);
                    }
                }
            }
            return interestingPos;
        }
      
        ......  
    }  

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

            WindowManagerService类的成员函数tmpRemoveWindowLocked将参数win所描述的窗口及其子窗口从WindowManagerService服务内部的窗口堆栈中删除,即从 WindowManagerService类的成员变量mWindows所描述的一个ArrayList中删除。

            如果每一个被删除的窗口在窗口堆栈中的位置比参数interestingPos的值小,那么WindowManagerService类的成员函数tmpRemoveWindowLocked还会将参数interestingPos的值减少1,这相当于是计算当删除参数win所描述的窗口及其子窗口之后,原来位于窗口堆栈中第interestingPos个位置的窗口现在位于窗口堆栈的位置,这个位置最终会作为WindowManagerService类的成员函数tmpRemoveWindowLocked的返回值。

           11. 在指定位置增加WindowState

           在指定位置增加WindowState是通过调用WindowManagerService类的成员函数reAddWindowLocked来实现的,如下所示:

    public class WindowManagerService extends IWindowManager.Stub
            implements Watchdog.Monitor {
        ......
    
        private final int reAddWindowLocked(int index, WindowState win) {
            final int NCW = win.mChildWindows.size();
            boolean added = false;
            for (int j=0; j<NCW; j++) {
                WindowState cwin = win.mChildWindows.get(j);
                if (!added && cwin.mSubLayer >= 0) {
                    ......
                    mWindows.add(index, win);
                    index++;
                    added = true;
                }
                ......
                mWindows.add(index, cwin);
                index++;
            }
            if (!added) {
                ......
                mWindows.add(index, win);
                index++;
            }
            mWindowsChanged = true;
            return index;
        }
    
        ......
    }

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

            参数win描述的即为要增加的WindowState对象,而参数index描述的即为要将参数win所描述的WindowState对象及其子WindowState对象要增加到窗口堆栈中的起始位置。

           由于参数win所描述的WindowState对象的子WindowState对象的成员变量mSubLayer的值可能会小于0,也可能大于0。大于0的子WindowState对象位于参数win所描述的WindowState对象的上面,而小于0的子WindowState对象位于参数win所描述的WindowState对象的下面。因此,WindowManagerService类的成员函数reAddWindowLocked先增加那些小于0的子WindowState对象,接着再增加参数win所描述的WindowState对象,最后增加那些大于0的子WindowState对象。

            假设WindowManagerService类的成员函数reAddWindowLocked一共在窗口堆栈中增加了N个WindowState对象,那么它的返回值就等于index + N,这样调用者就可以知道参数win所描述的WindowState对象及其子WindowState对象在窗口堆栈中的最高位置是多少

            基于第9、第10和第11这三操作,可以组合成很多其它的WindowState操作,如接下来的第12、第13、第14和第15个操作所示。

            12. 将一个WindowState对象及其所有子WindowState对象增加到窗口堆栈中

             将一个WindowState对象及其所有子WindowState对象增加到窗口堆栈中是通过调用WindowManagerService类的成员函数reAddWindowToListInOrderLocked来实现的,如下所示:

    public class WindowManagerService extends IWindowManager.Stub
            implements Watchdog.Monitor {
        ......
    
        private void reAddWindowToListInOrderLocked(WindowState win) {
            addWindowToListInOrderLocked(win, false);
            // This is a hack to get all of the child windows added as well
            // at the right position.  Child windows should be rare and
            // this case should be rare, so it shouldn't be that big a deal.
            int wpos = mWindows.indexOf(win);
            if (wpos >= 0) {
                ......
                mWindows.remove(wpos);
                mWindowsChanged = true;
                reAddWindowLocked(wpos, win);
            }
        }
    
        ......
    }

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

            为了得到参数win所描述的WindowState对象的子WindowState对象在窗口堆栈中的起始位置,WindowManagerService类的成员函数reAddWindowToListInOrderLocked首先将参数win所描述的WindowState对象增加到窗口堆栈中,这是通过调用前面所分析的成员函数addWindowToListInOrderLocked来实现的,目的是为了获得它在窗口堆栈的位置。有了这个位置之后,WindowManagerService类的成员函数reAddWindowToListInOrderLocked就可以调用前面所分析的成员函数reAddWindowLocked来将WindowState对象及其所有子WindowState对象增加到窗口堆栈中去了,不过在调用之前,要先将参数win所描述的WindowState对象从窗口中堆栈删除。

            13. 将一个WindowToken对象对应的所有WindowState对象及其子WindowState对象增加到窗口堆栈的指定位置上

             将一个WindowToken对象对应的所有WindowState对象都增加到窗口堆栈中是通过调用WindowManagerService类的成员函数reAddAppWindowsLocked来实现的,如下所示:

    public class WindowManagerService extends IWindowManager.Stub
            implements Watchdog.Monitor {
        ......
    
        private final int reAddAppWindowsLocked(int index, WindowToken token) {
            final int NW = token.windows.size();
            for (int i=0; i<NW; i++) {
                index = reAddWindowLocked(index, token.windows.get(i));
            }
            return index;
        }
    
        ......
    }

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

            与参数token所描述的WindowToken对象所对应的WindowState对象保存在它的成员变量windows所描述的一个ArrayList中。通过遍历这个ArrayList,就可以将与参数token所描述的WindowToken对象所对应的WindowState对象及其子WindowState对象都增加到窗口堆栈的指定的起始位置上去,这是通过调用前面所分析的成员函数reAddWindowLocked来实现的。

            参数index描述的便是最初指定的起始位置,每一次调用WindowManagerService类的成员函数reAddWindowLocked之后,它的值都便会被更新为下一个WindowState对象及其子WindowState对象要增加到窗口堆栈中的位置。

            最后,WindowManagerService类的成员函数reAddAppWindowsLocked将与参数token所描述的WindowToken对象所对应的WindowState对象在窗口堆栈中的最高位置加1后的得到结果返回给调用者

           14. 将一个AppWindowToken对象所对应的WindowState对象及其子 WindowState对象移动到窗口堆栈的指定位置上

            将一个AppWindowToken对象所对应的WindowState对象及其子 WindowState对象移动到窗口堆栈的指定位置上是通过调用WindowManagerService类的成员函数moveAppWindowsLocked来实现的,如下所示:

    public class WindowManagerService extends IWindowManager.Stub
            implements Watchdog.Monitor {
        ......
    
        private void moveAppWindowsLocked(AppWindowToken wtoken, int tokenPos,
                boolean updateFocusAndLayout) {
            // First remove all of the windows from the list.
            tmpRemoveAppWindowsLocked(wtoken);
    
            // Where to start adding?
            int pos = findWindowOffsetLocked(tokenPos);
    
            // And now add them back at the correct place.
            pos = reAddAppWindowsLocked(pos, wtoken);
    
            if (updateFocusAndLayout) {
                if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
                    assignLayersLocked();
                }
                mLayoutNeeded = true;
                performLayoutAndPlaceSurfacesLocked();
            }
        }
    
        ......
    }

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

            参数wtoken描述的是要移动其所对应的WindowState对象的一个AppWindowToken对象,而参数tokenPos描述的是该AppWindowToken对象在WindowManagerService服务内部的AppWindowToken列表中的新位置。

            WindowManagerService类的成员函数moveAppWindowsLocked首先调用前面所分析的成员函数tmpRemoveAppWindowsLocked来移除所有与参数wtoken所描述的AppWindowToken对象所对应的WindowState对象,接着再调用也是前面所分析的成员函数findWindowOffsetLocked来获得参数wtoken所描述的AppWindowToken对象所对应的WindowState对象在窗口堆栈中的起始位置。有了这个起始位置之后,就可以也是前面所分析的成员函数reAddAppWindowsLocked来将参数wtoken所描述的AppWindowToken对象所对应的WindowState对象及其子WindowState对象移动到窗口堆栈上去了。

            最后,如果参数updateFocusAndLayout的值等于true,那么WindowManagerService类的成员函数moveAppWindowsLocked还会更新系统当前获得焦点的窗口,以及重新计算系统中的所有窗口的Z轴位置以及重新布局系统中的所有窗口,这三个操作分别是通过调用WindowManagerService类的成员函数updateFocusedWindowLocked、assignLayersLocked和performLayoutAndPlaceSurfacesLocked来实现的。

            15. 将一组AppWindowToken对象所对应的WindowState对象及其子 WindowState对象移动到窗口堆栈的指定位置上

             将一组AppWindowToken对象所对应的WindowState对象及其子WindowState对象移动到窗口堆栈的指定位置上是通过调用WindowManagerService类的另外一个版本的成员函数moveAppWindowsLocked来实现的,如下所示:

    public class WindowManagerService extends IWindowManager.Stub
            implements Watchdog.Monitor {
        ......
    
        private void moveAppWindowsLocked(List<IBinder> tokens, int tokenPos) {
            // First remove all of the windows from the list.
            final int N = tokens.size();
            int i;
            for (i=0; i<N; i++) {
                WindowToken token = mTokenMap.get(tokens.get(i));
                if (token != null) {
                    tmpRemoveAppWindowsLocked(token);
                }
            }
    
            // Where to start adding?
            int pos = findWindowOffsetLocked(tokenPos);
    
            // And now add them back at the correct place.
            for (i=0; i<N; i++) {
                WindowToken token = mTokenMap.get(tokens.get(i));
                if (token != null) {
                    pos = reAddAppWindowsLocked(pos, token);
                }
            }
    
            if (!updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES)) {
                assignLayersLocked();
            }
            mLayoutNeeded = true;
            performLayoutAndPlaceSurfacesLocked();
    
            //dump();
        }
    
        ......
    }

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

            这个操作与前面分析的第14个操作是类似,区别只在于前者是批量地移动一组AppWindowToken对象所对应的WindowState对象及其子 WindowState对象,而后者是只移动一个AppWindowToken对象所对应的WindowState对象及其子WindowState对象,此外,前者总是会调用WindowManagerService类的成员函数updateFocusedWindowLocked、assignLayersLocked和performLayoutAndPlaceSurfacesLocked来更新系统当前获得焦点的窗口、以及重新计算每一个窗口的Z轴位置,并且对这些窗口进行重新布局。

            至此,我们就分析完成WindowManagerService服务组织系统中的窗口的方式了。从分析的过程中,可以得到以下结论:

            1. WindowManagerService服务维护有一个AppWindowToken堆栈和一个WindowState堆栈,它们与ActivityManagerService服务维护的Actvity堆栈是有关相同的Z轴位置关系的。

            2. ActivityManagerService服务中的每一个ActivityRecord对象在WindowManagerService服务中都对应有一个AppWindowToken对象,而WindowManagerService服务中的每一个AppWindowToken对象都对应有一组WindowState对象。

            3. 在WindowState堆栈中,AppWindowToken堆栈中的第i+1个AppWindowToken对象所对应的WindowState对象都位于第i个AppWindowToken对象所对应的WindowState对象的上面。

            4. 一个WindowState对象可以附加在另外一个WindowState对象上面,此外,一个WindowState对象还可以有子WindowState对象,它们都是与同一个AppWindowToken对象或者WindowToken对象所对应的。

            5. WindowManagerService服务有两个特殊的WindowToken,它们分别用来描述系统中的输入法窗口令牌和壁纸窗口令牌,其中,输入法窗口位于需要输入法的窗口的上面,而壁纸窗口位于需要壁纸的窗口的下面。

            最后,我们可以将WindowManagerService服务中的AppWindowToken理解成一个Activity组件令牌,而将它所对应的WindowState对象理解成一个Activity窗口。有了这些概念之后,就为学习WindowManagerService服务的各种实现打下坚实的基础。在接下来的两篇文章中,我们就会在本文的基础上,继续分析WindowManagerService服务是如何管理系统中的输入法窗口和壁纸窗口的,敬请关注!

    老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

    展开全文
  • python告白代码,只属于程序员的浪漫

    千次阅读 多人点赞 2019-11-16 19:39:42
    写在前头: ...当然秃顶也是必须的,更狠的吐槽还有邋里邋遢,懂浪漫,不知人情世故!开始可能只是幽默玩笑,后面慢慢就越传越多,大家便信以为真!可是程序员真的是这样吗?随着现在编程这个行业...
  • Android8.0多窗口调研

    千次阅读 2018-05-10 11:13:41
    Android8.0多窗口调研一、概述Android8.0上面原生的多窗口功能支持四种模式:全屏、分屏、画中画、...以下分析基于Android8.0代码。二、原理框架Android原生多窗口是多Stack方案,即存在多个ActivityStack。Activi...
  • 在Android系统中,Activity组件在启动之后,并且在它的窗口显示出来之前,可以显示一个启动窗口。这个启动窗口可以看作是Activity组件的预览窗口,是由WindowManagerService服务统一管理的,即由...
  • Windows 窗口透明知识点

    千次阅读 2019-05-18 18:38:14
    窗口不能是Child类型 要使使窗体拥有透明效果,首先要有WS_EX_LAYERED扩展属性,方法可以在CreateWindowEx时指定,也可以SetWindowLong动态设置。如下代码可设置分层窗口: LONG style = GetWindowLong(m_hWnd, GWL_...
  • Android7.0 多窗口你值得拥有

    万次阅读 2016-09-23 16:52:29
    窗口分屏其实在国内并陌生,已经有一些手机和平板搭载了“分屏多任务”和”APP窗口化”功能,但这些都是手机厂商自主定制系统中添加的功能,并非安卓原生所有,因此他们的实现方式可能会一样,但是现在google原始...
  • 在Android系统中,同一时刻只有一个Activity组件是处于激活状态的,因此,当ActivityManagerService服务激活了一个新的Activity组件时,它就需要通知WindowManagerService服务将该Activity组件的窗口显示出来,这会...
  • 在前一文中,我们分析了Activity组件的切换...再进一步地,如果一个窗口是附加在另外一个窗口之上的,那么被附加窗口所设置的动画也会同时传递给该窗口。本文就详细分析WindowManagerService服务显示窗口动画的原理。
  • 前文从总结恶意代码检测技术,包括恶意代码检测的对象和策略、特征值检测技术、校验和检测技术、启发式扫描技术、虚拟机检测技术和主动防御技术。这篇文章将介绍基于机器学习的恶意代码检测技术,主要参考郑师兄的...
  • 如果两个网页同源,就无法拿到对方的DOM。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。 比如,父窗口运行下面的命令,如果iframe窗口不是同源,就会报错。 document....
  • #Python3中pywin32中常用代码解释

    千次阅读 2018-10-22 23:13:51
    Python3中pywin32中常用代码解释 摘要 本文介绍 Win32 版本的 Microsoft Windows 操作系统提供的桌面窗口、顶层窗口和子窗口,以及它们之间的层级关系;解释了应用程序如何游历窗口结构,如何控制桌面上显示的窗口...
  • mfc窗口继承

    千次阅读 2014-01-23 11:48:37
    窗口的继承,子类窗口可以拥有父类窗口的一些属性、风格和方法等,将极大的简化代码量,本例将完成一个mfc窗口继承的实例: 1.新建一个基于对话框的dialog,作为父类窗口,本例名称TemplateDlg TemplateDlg.h: /...
  • 在Android系统中,窗口是有分组概念的,例如,Activity中弹出的所有PopupWindow会随着...之间已经简单介绍了窗口类型划分:应用窗口、子窗口、系统窗口,Activity与Dialog都属于应用窗口,而PopupWindow属于窗口
  • macOS 窗口窗口控制器教程

    千次阅读 2017-06-02 21:34:33
    macOS 窗口窗口控制器教程原文:Windows and WindowController Tutorial for macOS 更新:本教程由Warren Burton更新到Xcode8 和 Swift 3。原教程是Gabriel Miro编写的。 窗口是所有macOS程序的用户界面的“容器...
  • Android 多窗口框架全解析

    万次阅读 2017-07-13 10:31:52
    本文基于AOSP Android-7.1.1-R9代码进行分析。 Android N的的多窗口框架中,总共包含了三种模式。 Split-Screen Mode: 分屏模式。 Freeform Mode 自由模式:类似于Windows的窗口模式。 Picture In Picture Mode:画...
  • Flink之窗口

    千次阅读 2020-05-22 00:03:56
    第一个代码段指的是键控流,第二个代码段指的是非键控流。两者唯一的区别是keyBy(...)之后调用window(...)的称为成为键控流;DataStream直接调用windowAll(...)的称为非键控流。 Keyed Windows stream .keyBy...
  • 关于jsp网页弹出窗口

    千次阅读 2014-10-17 14:38:44
    关于jsp网页弹出窗口    各种弹出页面的设计 【1、普通的弹出窗口】  其实代码非常简单:    window.open ('page.html')  -->     因为这是一段javascripts代码,所以它们...
  • [Flink]Flink1.3 Stream指南三 窗口分配器

    千次阅读 2017-10-24 11:11:36
    1.4版本:Flink1.4 窗口概述Windows(窗口)是处理无限数据流的核心。Windows将流分解成有限大小的"桶",在上面我们可以进行计算。本文档重点介绍如何在Flink中处理窗口,以及如何从它提供的功能中获得最大...
  • 自定义窗口

    千次阅读 2010-05-18 15:12:00
    最近在弄一个测试程序,希望可以创建一个自定义的窗口来画图,由于在console工程中,所以想自己创建一个窗口,在路上........ http://hi.baidu.com/xiuyuan132/blog/item/12bb3826c9e6653f8644f90a.html该函数用来...
  • 代码克隆是非常相似的独立代码片段。 在已开发一段时间的应用程序中,常会出现这种现象。 克隆提高了更改应用程序的难度,因为你必须找到并更新多个片段。 Visual Studio Ultimate 或 Visual Studio Premium ...
  • Android窗口管理

    千次阅读 2015-03-11 16:31:10
    一、 概述 ...在Android系统中,从设计的角度来看,窗口管理系统是基于C/S模式的。整个窗口系统分为服务端和客户端两大部分,客户端负责请求创建窗口和使用窗口,服务端完成窗口的维护,...在Client端,并是直
  • jsp 弹出窗口设置大全

    千次阅读 2014-07-18 18:13:53
    【1、普通的弹出窗口】  其实代码非常简单:    window.open ('page.html')  -->     因为这是一段javascripts代码,所以它们应该放在标签和之间。是对一些版本低的浏览器起作用,在这些老...
  • 在前两文中,我们分析了Activity组件的窗口对象和视图对象的创建过程。Activity组件在其窗口对象和视图对象创建完成之后,就会请求与WindowManagerService建立一个连接,即请求WindowManagerService为其增加一个...
  • AppCan基础——主窗口 & 辅窗口机制

    千次阅读 2012-08-02 17:25:01
    1、主窗口 & 辅窗口机制 常用的手机应用(App)设计界面,都分为top,content,bottom三块区域,但是在android2.1以下,ios4以下版本的webkit存在如下问题: 支持div滚动条 Css样式的{position:fixed}属性...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 96,523
精华内容 38,609
关键字:

下列不属于代码窗口