-
Notifications
You must be signed in to change notification settings - Fork 116
WMS核心分析
WindowManagerService(简称WMS)是负责Android的窗口管理,但是它其实只负责管理,比如窗口的添加、移除、调整顺序等,至于图像的绘制与合成之类的都不是WMS管理的范畴,WMS更像在更高的层面对于Android窗口的一个抽象,真正完成图像绘制的是APP端,而完成图层合成的是SurfaceFlinger服务。
这里通过一个简单的悬浮窗口来探索一下大概流程:
TextView textView = new TextView(context);
...
// 设置颜色 样式
WindowManager mWindowManager## = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();
wmParams.type = WindowManager.LayoutParams.TYPE_TOAST;
wmParams.format = PixelFormat.RGBA_8888;
wmParams.width = 800;
wmParams.height = 800;
mWindowManager.addView(textView, wmParams);
以上代码可以在主屏幕上添加一个TextView并展示,并且这个TextView独占一个窗口。在利用WindowManager.addView添加窗口之前,TextView的onDraw不会被调用,也就说View必须被添加到窗口中,才会被绘制,或者可以这样理解,只有申请了依附窗口,View才会有可以绘制的目标内存。当APP通过WindowManagerService的代理向其添加窗口的时候,WindowManagerService除了自己进行登记整理,还需要向SurfaceFlinger服务申请一块Surface画布,其实主要是画布背后所对应的一块内存,只有这一块内存申请成功之后,APP端才有绘图的目标,并且这块内存是APP端同SurfaceFlinger服务端共享的,这就省去了绘图资源的拷贝,示意图如下:
以上是抽象的图层对应关系,可以看到,APP端是可以通过unLockCanvasAndPost直接同SurfaceFlinger通信进行重绘的,就是说图形的绘制同WMS没有关系,WMS只是负责窗口的管理,并不负责窗口的绘制
添加一个只有TextView的悬浮窗Window,代码如下:
private void addTextViewWindow(Context context){
TextView mview=new TextView(context);
...<!--设置颜色 样式-->
<!--关键点1-->
WindowManager mWindowManager = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();
<!--关键点2-->
wmParams.type = WindowManager.LayoutParams.TYPE_TOAST;
wmParams.format = PixelFormat.RGBA_8888;
wmParams.width = 800;
wmParams.height = 800;
<!--关键点3-->
mWindowManager.addView(mview, wmParams);
}
关键点1获取WindowManagerService的代理对象--WindowManagerImpl的实例。接着通过WindowManager.LayoutParams.type指定窗口类型为TOAST。最后调用addView将View添加到WMS。
// WindowManagerImpl
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
WindowManagerImpl中委托WindowManagerGlobal来进行添加。WindowManagerGlobal是一个单利,添加窗口精简后的代码如下:
// WindowManagerGlobal
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
// 创建ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
// 将View添加到ViewRootImpl
try {
root.setView(view, wparams, panelParentView);
}
...
}
WindowManagerGlobal添加View时首先创建了一个ViewRootImpl。ViewRootImpl是Window和View之间的通信纽带,负责将View添加到WMS,处理WMS传入的触摸事件,通知WMS更新窗口大小,以及负责通知View的绘制与更新。
ViewRootImpl的构造方法如下:
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
mWindow = new W(this);
}
通过WindowManagerGlobal.getWindowSession获取Binder服务代理WindowSession,它是APP端向WMS发送消息的通道。mWindow是一个W extends IWindow.Stub Binder服务对象,可视为APP端的窗口对象,主要作用是传递给WMS,并作为WMS向APP端发送消息的通道。
首先通过getWindowManagerService 获取WMS的代理,之后通过WMS的代理在服务端open一个Session,并在APP端获取该Session的代理:
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
<!--关键点1-->
IWindowManager windowManager = getWindowManagerService();
<!--关键点2-->
sWindowSession = windowManager.openSession(***)
...
return sWindowSession;
}
}
getWindowManagerService其实就是获得WindowManagerService的代理。openSession会打开一个Session返回给APP端。而Session extends IWindowSession.Stub,是一个Binder通信的Stub端,封装了每一个Session会话的操作。
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
接下调用Session的addToDisplayWithoutInputChannel方法添加一个窗口:
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
最终又通过WMS来addWindow。
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
InputChannel outInputChannel) {
...
synchronized(mWindowMap) {
...
// 1.不能重复添加窗口
if (mWindowMap.containsKey(client.asBinder())) {
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
// 2.对于子窗口类型的处理 1、必须有父窗口 2,父窗口不能是子窗口类型
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
parentWindow = windowForClientLocked(null, attrs.token, false);
if (parentWindow == null) {
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}}
...
boolean addToken = false;
//3.根据IWindow 获取WindowToken WindowToken是窗口分组的基础,每个窗口必定有一个分组
WindowToken token = mTokenMap.get(attrs.token);
//4.对于Toast类系统窗口,其attrs.token可以看做是null, 如果目前没有其他的类似系统窗口展示,token仍然获取不到,仍然要走新建流程
if (token == null) {
...
token = new WindowToken(this, attrs.token, -1, false);
addToken = true;
}
...
// 5. 新建WindowState,WindowState与窗口是一对一的关系,可以看做是WMS中与窗口的抽象实体
WindowState win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
...
if (addToken) {
mTokenMap.put(attrs.token, token);
}
win.attach();
mWindowMap.put(client.asBinder(), win);
...
addWindowToListInOrderLocked(win, true);
return res;
}
至此,APP向WMS添加窗口的流程就结束了。但是WMS还要向SurfaceFlinger申请Surface才算真正完成窗口的添加。
- JMM与volatile关键字
- synchronized的实现原理
- synchronized等待与唤醒机制
- AQS的实现原理
- ReentrantLock的实现原理
- ReentrantLock等待与唤醒机制
- CAS、Unsafe类以及Automic并发包
- ThreadLocal的实现原理
- 线程池的实现原理
- Java线程中断机制
- 多线程与并发常见面试题
- Android基础知识汇总
- MVC、MVP与MVVM
- SparseArray实现原理
- ArrayMap的实现原理
- SharedPreferences
- Bitmap
- Activity的启动模式
- Fragment核心原理
- 组件化项目架构搭建
- 组件化WebView架构搭建
- 为什么 Activity.finish() 之后 10s 才 onDestroy ?
- Binder与AIDL
- Binder实现原理
- Android系统启动流程
- InputManagerService
- WindowManagerService
- Choreographer详解
- SurfaceFlinger
- ViewRootImpl
- ActivityManagerService
- APP启动流程
- PMS安装与签名校验
- Dalvik与ART
- 内存优化策略
- UI界面及卡顿优化
- App启动优化
- ANR问题
- 包体积优化
- APK打包流程
- 电池电量优化
- Android屏幕适配
- 线上性能监控1--线上监控切入点
- 线上性能监控2--Matrix实现原理
- Glide实现原理
- OkHttp实现原理
- Retrofit实现原理
- RxJava实现原理
- RxJava中的线程切换与线程池
- LeakCanary实现原理
- ButterKnife实现原理
- ARouter实现原理
- Tinker实现原理
- 2. 两数相加
- 19.删除链表的倒数第 N 个结点
- 21. 合并两个有序链表
- 24. 两两交换链表中的节点
- 61. 旋转链表
- 86. 分隔链表
- 92. 反转链表 II
- 141. 环形链表
- 160. 相交链表
- 206. 反转链表
- 206 反转链表 扩展
- 234. 回文链表
- 237. 删除链表中的节点
- 445. 两数相加 II
- 面试题 02.02. 返回倒数第 k 个节点
- 面试题 02.08. 环路检测
- 剑指 Offer 06. 从尾到头打印链表
- 剑指 Offer 18. 删除链表的节点
- 剑指 Offer 22. 链表中倒数第k个节点
- 剑指 Offer 35. 复杂链表的复制
- 1. 两数之和
- 11. 盛最多水的容器
- 53. 最大子序和
- 75. 颜色分类
- 124.验证回文串
- 167. 两数之和 II - 输入有序数组 -169. 多数元素
- 189.旋转数组
- 209. 长度最小的子数组
- 283.移动0
- 303.区域和检索 - 数组不可变
- 338. 比特位计数
- 448. 找到所有数组中消失的数字
- 643.有序数组的平方
- 977. 有序数组的平方