WindowManagerService
WindowManagerSerivce有三个主要功能:
窗口管理系统:负责窗口的启动、添加、删除、大小、层级顺序等,它会为每个窗口分配一块Surface以供绘制。核心成员有DisplayContent、WindowToken、WindowState
窗口动画系统:窗口切换时的动画。核心成员:WindowAnimator、Choreographer
输入系统的中转站:从Android Framework – InputManagerService中我们知道输入事件起于InputManagerService及底层系统,由每个Window的RootViewImpl来接收处理,而在寻找接收的Window的时候自然需要WMS这个窗口管理者来协助并中转了。
PS:通过Android Framework–开机启动流程我们知道WMS是Android启动后期开了一个ServerThread线程来开启的各种服务之一,主要调用WindowManagerService.main()来实例化WMS
窗口管理系统
Android解析WindowManager(一)WindowManager体系
Android解析WindowManager(二)Window的属性
Android解析WindowManager(三)Window的添加过程
窗口动画系统
窗口(主要是Activity)的绘制
WindowManagerService相关的主要是窗口的层级排序,以及为窗口管理一块Surface,那么具体到每个Window它又是怎么绘制的呢。
Window 有三种类型,分别是应用 Window、子 Window 和系统 Window。
应用类 Window :对应一个 Acitivity,一般就是PhoneWindow
子 Window :不能单独存在,需要依附在特定的父 Window 中,比如常见的一些 Dialog 就是一个子 Window。
系统 Window : 需要声明权限才能创建的 Window,比如 Toast 和系统状态栏都是系统 Window
这里我们只考虑应用类Window,PhoneWindow。看一张经典图
从Android Framework – ActivityManagerService中可以知道,一个App的冷启动会新建一个进程,利用LoadedAPK加载APK资源,调用handleLaunchActivity来启动一个Activity。
ActivityThread.handleLaunchActivity
|-- performLaunchActivity
|–Instrumentation.newActivity创建Activity实例
|–创建Context对象与Activity关联
|–activity.attach()
|–Instrumentation.callActivityOnCreate调用onCreate
上述节点做的事情大致如下
Android应用Activity、Dialog、PopWindow、Toast窗口添加机制及源码分析
布局添加后我们的View又是怎么绘制出来的呢,这部分就得回到Window开始讲起了,先看下图
每个Activity最外层都是一个PhoneWindow,PhoneWindow向WindowManagerGlobal发起请求,调用管理自己的那个ViewRootImpl来绘制自身。忽略层层关系,ViewRootImpl管理PhoneWindow的根View(DercorView)的绘制(当然ViewRootImpl也管其他的,这里只关心绘制),一切的初始都是performTraversals()
onResume中会调用makeVisible()使用WindowManager.addView(DecorView)添加Activity的根View(即DecorView或ViewRoot),WindowManager.addview会调用WindowManagerGlobal.addView(一个activity只有一个wm,一个app只有一个wmg,wmg会保存所有activity的window和VRI及其params信息) 会为Activity new一个ViewRootImpl,并把decorview设置到VRI里,同时保存decorview、VRI、params。
这里就是我们熟悉的onMeasure onLayout onDraw了,DecorView是一个FrameLayout,把递归的去测量 布局 绘制子View。值得一提的是最终的绘制方法有两种一种用软件去绘制用的是Surface另外一种则是硬件加速去绘制(不了解)。
PS:
invalidate和postInvalidate 和requestLayout的区别
- invalidate 只能在UI线程中调用 强制刷新视图onDraw 层层上传到ViewRootImpl最终调用scheduleTraversal,scheduleTraversal会发送异步消息调用doTraversal,doTraversal调用performTraversals进行重绘
- postInvalidate 允许在子线程调用 ,其实它只是通过Handler回到主线程调用invalidate
- rquestLayout 最终其实也是调用的scheduleTraversal,但是他设置了不同的标志位,导致重绘过程只会调用measure layout 而不调用draw
Dialog报错BadTokenException
- Dialog使用的是Activity传进来的Context的token,所以如果Activity销毁了,Dialog显示会报错 找不到token;
而如果传入的Context使用Application的或Service的 则因为Dialog类型是TYPE_APPLICATION的,也拿不到对应类型的token所以报错;
Dialog必须依附在Activity上,与activity共用一个WindowManager
Android应用层View绘制流程与源码分析
Android应用setContentView与LayoutInflater加载解析机制源码分析
SurfaceFlinger
当然我们调用Surface来绘制View的时候,底层又是怎么走的呢?答案就是SurfaceFlinger。这部分比较复杂,记录下我了解的皮毛。
Java层的Surface会通过JNI调用android_view_surface.cpp调用底层的Surface系统,底层Surface会将一帧数据写入Back GraphicBuffer中,可以想象成是一个画面的像素,而且看到Back就知道这是备份,此时SurfaceFlinger正在显示的是Font GraphicBuffer帧的画面,然后SurfaceFlinger会过来取下一帧,理想状态下Back 和 Font两帧会对调,把刚刚填充了新数据的Back GraphicBuffer取走进行显示,把Font GraphicBuffer接收Surface的下一帧数据填充。SurfaceFlinger取到数据后对每层Layer进行处理 混合最终写入驱动显示在屏幕上。