聊聊 Android 的 GUI 系统( 三 )

  • systemUiVisibility 表示系统 UI 的可见性
  • Flags Description SYSTEM_UI_FLAG_VISIBLE = 0 请求显示系统UI,默认状态 SYSTEM_UI_FLAG_LOW_PROFILE = 0x00000001 低能模式,状态栏上的一些图标会被隐藏,游戏、阅读、视频播放等沉浸式应用会需要 SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002 请求隐藏底部导航栏 SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004 请求全屏显示,状态栏会被隐藏,底部导航栏不会被隐藏,效果和WindowManager.LayoutParams.FLAG_FULLSCREEN相同 SYSTEM_UI_FLAG_IMMERSIVE = 0x00000800 这个flag只有当设置了SYSTEM_UI_FLAG_HIDE_NAVIGATION才起作用 。如果没有设置这个flag,任意的View相互动作都退出SYSTEM_UI_FLAG_HIDE_NAVIGATION模式 。如果设置就不会退出 SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 0x00001000 这个flag只有当设置了SYSTEM_UI_FLAG_FULLSCREEN|SYSTEM_UI_FLAG_HIDE_NAVIGATION时才起作用 。如果没有设置这个flag,任意的View相互动作都会退出SYSTEM_UI_FLAG_FULLSCREEN|SYSTEM_UI_FLAG_HIDE_NAVIGATION模式,如果设置就不受影响 SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000 状态栏浅色背景模式,文字为黑色,Android 6.0以前(api < 23)不支持 SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100 请求系统UI布局稳定状态 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400 让View全屏显示,Layout会被拉伸到StatusBar下面,不包含NavigationBar SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200 让View全屏显示,Layout会被拉伸到NavigationBar下面
    上面这些属性除了 systemUiVisibility 相关的是定义在 View 中的,其他的都是定义在 WindowManager 中的
    我是WindowManager我是一个继承于 ViewManager 的接口,WindowManagerImpl 是我的具体实现类 。ViewManager 中定义了与 View 交互的接口函数 addView()、updateViewLayout()、removeView() ,应用程序通过(WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)获取到 WindowManager 实例后,就可以通过addView()将 View 添加到 WMS 中去 。
    但是我只是一个接口啊,就连我的实现者 WindowManagerImpl 也没有任何持有 WSM 的影子啊 。那 View 是如何添加到 WMS 中去的呢?既然我们两个没法暗通款曲,那就索性寻个媒婆来明媒正娶 。这点,ViewRootImpl 是极其专业的,于是我便找了它 。
    我是ViewRootImpl我是一个中介,负责管理整颗 View 树的同时,也担负着与 WMS 进行 IPC 通讯的重任 。具体而言,我会通过 IWindowSession 建立双方的桥梁 。
    从实现上来说,在构造函数中,我会通过 WindowManagerGlobal.getWindowSession()来打开一个IWindowSession 对象来与 WMS 的可用连接 。IWindowSession 是一个 IBinder 接口,它定义了一系列与 window manager 交互的交互方式,如此一来,当应用程序调用 setView 等方法时,我就可以利用它来发起一个服务请求 。IWindowSession 的服务端(Session.java)便会响应这个请求,从而调用 WMS 的 addWindow()来传递给 WMS 处理 。
    我是WindowManagerService我和 AMS 等 Service 一样,是由 SystemServer 启动的系统服务的一部分 。由于我是由 SystemServer 启动的,启动时机相对较晚,如果在 SystemServer 还没运行之前,我是无能为力的 。比如在开机时候显示的开机动画,那时候我还没运行起来,所以这时候的显示则是由 BootAnimation 直接通过 OpenGL ES 与SurfaceFlinger 的配合来完成的 。原则上我只负责“窗口”的层级和属性,之所以能够将 Window 内容显示出来,也是由于我与 SurfaceFlinger 沟通后,SufaceFlinger 才真正将窗口数据合成并最终显示在屏幕上的缘故 。
    从某种方面来说,我可是整个 Android UI 体系的大导演呢,因为我会根据实际情况来安排每个演员(Window)的排序站位,谁前谁后,怎么进场,如何出场等,目的当然也是为了将舞台效果和视觉美感表现得更佳,从而呈现给观众 。我并不关心这里面的演员是谁,从源码角度来说,我不关心 View 树,或者说这个 window 所表达的具体类容是什么,我只要知道需要显示的界面大小,层级值等即可,而这些已经作为 WindowManager.LayoutParams 参数传递给我了 。
    前面说到,WMS 还需要通知 SurfaceFlinger,才能把正确的结果及时的呈现给“观众” 。由于 SurfaceFlinger 绘制 UI 界面需要有“画板”—— BufferQueue 的支持,BufferQueue 在 SurfaceFlinger 中对应的是 Layer,在 Java 层对应的则是 Surface( Surface 持有 BufferQueue 的实现接口—— IGraphicBufferProducer ),因此,无论是系统窗口还是应用窗口,都必须向 SurfaceFlinger 申请相应的 Layer,进而得到图形缓冲区的使用权 。
    WMS、AMS 与 Activity 间的联系Activity 运行在应用程序进程中,而 AMS 与WMS 则运行在系统相关进程中,它们之间的通信需要 Binder 的支持 。应用程序访问 WMS 的服务首先要通过 ServiceManager,因为 WMS 是实名 Binder Server;WMS 还针对每个 Activity 提供了一种匿名的实现,即 IWindowSession 。


    推荐阅读