Android Window 初探

228 阅读4分钟

窗口是什么

Android系统中的窗口是屏幕上的一块用于绘制各种UI元素并可以响应应用户输入的一个矩形区域。

从原理上来讲,窗口的概念是独自占有一个 Surface 实例的显示区域。

例如Dialog、Activity的界面、壁纸、状态栏以及Toast等都是窗口。

一个 Activity 通过 Surface 来显示自己:

  • Surface 是一块画布, 应用可以随心所欲地通过 Canvas 或者 OpenGL 在其上作画。
  • 然后通过 SurfaceFlinger 将多块 Surface 的内容按照特定的顺序(z-order)进行混合并输出到 FrameBuffer,从而显示给用户。

WMS 这里充当一个管家的角色

既然每个窗口都有一块Surface供自己涂鸦,必然需要一个角色对所有窗口的Surface进行协调管理。于是,WMS便应运而生。WMS为所有窗口分配Surface,掌管Surface的显示顺序(Z-order)以及位置尺寸,控制窗口动画,并且还是输入系统的一重要的中转站。

说明

一个窗口拥有显示和响应用户输入这两层含义

本章将深入分析WMS的两个基础子系统的工作原理:

  • 布局系统(Layout System) :计算与管理窗口的位置、层次。
  • 动画系统(Animation System) :根据布局系统计算的窗口位置与层次渲染窗口动画。

例子

// SampleWindow.java
​
​
public class SampleWindow {
    public static void main(String[] args) {
        try {
            // 程序主入口
            new SampleWindow().Run();
        } catch (Exception e) {
            e.printStackTrace);
        }
    }
    
    // IWindowSession 是客户端向WMS请求窗口操作的中间代理,并且是进程唯一的
    IWindowSession mSesison = null;
    
    // InputChannel 是窗口接收用户输入事件的管道
    InputChannel  mInputChannel = new InputChannel();
    
    // 下面的三个 Rect 保存了窗口的布局结果。其中 mFrame 表示了窗口在屏幕上的位置与尺寸
    Rect mInsets = new Rect();
    Rect mFrame = new Rect();
    Rect mVisibleInsets = new Rect();
    Configuration mConfig = new Configuration();
    
    // 窗口的 Surface ,在此 Surface 上进行的绘制都将在此窗口上显示出来
    Surface mSurface = new Surface();
    
    // 用于在窗口上进行绘图的画刷
    Paint mPaint = new Paint();
    
    // 添加窗口所需的令牌
    IBinder mToken = new Binder();
    
    
    // 一个窗口对象,本例演示了如何将此窗口添加到 WMS 中,并在其上进行绘制操作
    MyWindow mWindow = new MyWindow();
    
    // WindowManager.LayoutParams 定义窗口的布局属性,包括位置、尺寸以及窗口类型等
    LayoutParams mLp = new LayoutParams();
    Choreographer mChoreographer = null;
    
    // InputHandler 用于从InputChnanel 接收案件事件并作出响应
    InputHandler mInputHandler = null;
    boolean mContinueAnime = true;
    public void Run() throws Exception {
        Looper.prepare();
        // 获取 WMS 服务
        IWindowManager wms = IWindowManager.Stub.asInterface (
                    ServiceManager.getService(Context.WINDOW_SERVICE));
        
        // 通过WindowManagerGlobal 获取进程唯一的IWindowSession 实例。
        // 用于向 WMS 发送请求。
        mSession = WindowManagerGlobal.getWindowSession(Looper.myLooper());
        
        
        // 获取屏幕分辨率
        IDisplayManager dm = IDisplayManager.Stub.asInterface(
                        ServiceManager.getService(Context.DISPLAY_SERVICE));
        
        DispalyInfo di = dm.getDisplayInfo(Display.DEFAULT_DISPLAY);
        Point scrnSize = new Point(di.appWidth, di.appHeight);
        
        // 初始化WindowManager.LayoutParams
        initLayoutParams(scrnSize);
        
        // 将新窗口添加到 WMS
        installWindow(wms);
        
        // 初始化 Choreographer 的实例,此实例为线程唯一。
        // 这个类的用法与 Handler 类似,不过它总是在 VSYC 同步时回调,所以比 Handler 更适合做动画的循环器
        mChoreographer = Choreographer.getInstance();
        
        // 开始处理第一帧的动画
        scheduleNextFrame();
        // 当前线程陷入消息循环,直到 Looper.quit()
        Looper.loop();
        
        // 标记不要继续绘制动画帧
        mContinueAnime = false;
        
        // 卸载当前 Window
        unistallWindow(wms);
    
    }
    
    ...
}
​
​

总结

在客户端创建一个窗口的步骤:

  1. 获取 IWindowSession 和 WMS 实例。 客户端可以通过 IWindowSession 向 WMS 发送请求。

  2. 创建并初始化 WindowManager.LayoutParams , 这里 LayoutParams 继承自 ViewGroup.LayoutParams类,并且扩展了一些属性。

    其中最重要的就是type属性。这个属性描述了窗口的类型,窗口类型是 WMS 对多个窗口进行 ZOrder 排序的依据。

  1. 向 WMS 添加一个窗口令牌(WindowToken),描述了一个显示行为,并且 WMS 要求每一个窗口必须隶属于某一个显示令牌。
  2. 向 WMS 添加一个窗口。必须在LayoutParams中指明此窗口所隶属于的窗口令牌,否则在某些情况下添加操作会失败。 在SampleWindow中,不设置令牌也可成功完成添加操作,因为窗口的类型被设为TYPE_SYSTEM_ALERT,它是系统窗口的一种。而对于系统窗口,WMS会自动为其创建显示令牌,故无须客户端操心。
  3. 向WMS申请对窗口进行重新布局(relayout)。所谓的重新布局,就是根据窗口新的属性去调整其Surface相关的属性,或者重新创建一个Surface(例如窗口尺寸变化导致之前的Surface不满足要求)。向WMS添加一个窗口之后,其仅仅是将它在WMS中进行注册而已。只有经过重新布局之后,窗口才拥有WMS为其分配的画布。有了画布,窗口之后就可以随时进行绘制工作了。

窗口的绘制过程:

  1. 通过 Surface.lock() 函数获取可以在其上作画的Canvas实例。
  2. 使用Canvas实例进行作画。
  3. 通过Surface.unlockCanvasAndPost()函数提交绘制结果。

窗口的本质

Surface 画布

当一块Surface显示在屏幕上时,就是用户所看到的窗口了。客户端向WMS添加一个窗口的过程,其实就是WMS为其分配一块Surface的过程,一块块Surface在WMS的管理之下有序地排布在屏幕上,Android才得以呈现出多姿多彩的界面。所以从这个意义上讲,WindowManagerService被称为SurfaceManagerService也说得通。