第八章,理解Window及WindowManager
Window及WindowManager
Window内部机制
Window创建过程
Window
是一个抽象类,实现类是PhoneWindow
, 创建Window只需要通过WindowManager
Window
的具体实现在WindowManagerService
中,WindowManager
和WindowManagerService
是一个IPC
过程Window
是View
的直接管理者
Window及WindowManager
通过WindowManager.addView
方法即可将View添加到Window,
mLayoutParams = new WindowManager.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
PixelFormat.TRANSPARENT);
//Window的属性
mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL//一般需要开启
| LayoutParams.FLAG_NOT_FOCUSABLE//Window不需要获取焦点,不需要各种输入事件,需要同时开启FLAG_NOT_TOUCH_MODAL,使事件下传.
| LayoutParams.FLAG_SHOW_WHEN_LOCKED;//让window显示在锁屏上
//window的类型,应用widow,子window,系统window
mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;
mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
mLayoutParams.x = 100;
mLayoutParams.y = 300;//显示位置
mWindowManager.addView(mFloatingButton, mLayoutParams);
Window
层级,z-ordered
.层级大的会覆盖在小的上面
.对应着mLayoutParams.type
参数
应用window: 1~99
子window: 1000~1999
系统window: 2000~2999
WindowManager
功能只有三个方法,即添加View,更新View,删除View
,
WindowManager
继承了ViewManager
,操作Window
的过程更像是操作Window
中的View.
Window内部机制
Window
是一个抽象概念,每个Window
对应着一个View和一个ViewRootImpl
ViewRootImpl
Window <-----------------> View
Window
并不是实际存在的,它是以View
的形式存在的.View
才是Window
存在的实体
Window的添加过程
//桥接模式
WindowManagerImpl#addView --> WindowManagerGlobal#addView
WindowManagerGlobal
添加过程:
- 检查参数是否合法
- 创建
ViewRoorImpl将View添加
到列表中. - 通过
ViewRoorImpl更新界面
(调用requestLayout
)
ViewRootImpl -> Session(WindowSession的实现类)
-> WindowManagerService#addWindow
Window的删除过程
WindowManagerImpl#removeView -> WindowManagerGlobal#removeView
-> findViewLocked -> removeViewLocked
-> ViewRootImpl#die -> dispatchDetachedFromWindow
- 通过
findViewLocked
查找待删除的View的索引. removeViewLocked
调用ViewRootImpl
的删除方法来做进一步的删除.removeView
和removeViewImmediate
,分别代表异步和同步删除
ViewRootImpl#die
中做了判断,如果是同步删除,则调用doDie
如果是异步删除,就发送一个MSG_DIE
,ViewRootImpl#Handler
会处理此消息并调用doDie
.
dispatchDetachedFromWindow
所做的工作
1. 垃圾回收相关的工作
2. 通过Session#remove
删除Window
,IPC
操作WindowManagerService#removeWindow
3. 调用View#onDetachedFromWindow
,终止动画,停止线程
4. 调用WindowManagerGlobal#doRemoveView
刷新数据.
Window的更新过程
依然是调用WindowManagerGlobal#updateViewLayout
.
- 更新View的
LayoutParams
,并替换掉老的LayoutParams
. - 通过
ViewRootImpl#setLayoutParams
更新ViewRootImpl的LayoutParams
ViewRootImpl#scheduleTraversals
重新测量,布局,绘制
Window创建过程
view
不能单独存在,必须附着在window
之上,有视图的地方就有widow.
Android
中可提供视图的地方有Activity
,Dialog
,Toast
,PopUpWindow
,菜单
.
Activity的Window创建过程
Activity
的启动过程最终由ActivityThread#performLaunchActivity
完成.
方法内部会通过类加载器加载Activity
的实例.并调用attach
方法关联运行过程中上下文环境.- 在
attach
方法中会调用PolicyManager#makeNewWindow
创建window对象,并设置回调接口. Activity
实现了Window.CallBack
,因此,当状态改变时会回调Activity.
PolicyManager
是一个策略类,方法都在IPolicy
接口中声明了.真正实现类是Policy
PolicyManager
与Policy
的关联,可能是在编译环节动态控制的
.- 通过
Activity#setContentView
将视图附着到window上
- 如果没有
DecorView
,就创建它- 将
View
添加到DecorView.mContentParent
中- 回调
Activity#onContentChanged
经过上面的步骤,DecorView
并没有添加到Window
,Activity
还无法接受输入事件,具体加入步骤如下:
Activity#attach -> PolicyManager#makeNewWindow
-> Activity#setContentView -> ActivityThread#handleResumeActivity -> Activity#onResume
-> Activity#makeVisible -> mWindowManager.addView//添加DecorView
Dialog的Window创建过程
- 创建
Window
,依然是PolicyManager#makeNewWindow
- 初始化
DecorView
,并将Dialog视图添加到DecorView
- 将
DecorView
添加到Window
中并显示 Dialog
关闭,则通过windowManager#removeViewImmediate(mDecor)
移除.
注意: 普通
Dialog
必须采用Activity
的Window
Toast的Window创建过程
Toast
的工作过程比Dialog
稍显复杂,Toast
具有定时取消这一功能(采用Handler
).- 在
Toast
内部有两类IPC过程
.一类,Toast->NotificationManagerService
,二类,NotificationManagerService -> Toast#TN
Toast
属于系统Window
,内部视图可以通过setView
来指定Toast
提供show和cancel
方法,他们是一个IPC过程
,都是通过NMS
完成TN是一个Binder类
,NMS
回调TN
来处理Toast的show和cancel
- 由于
TN
运行在Binder线程池
中,因此需要使用Handler(必须在有Looper的线程中弹出Toast)
LONG_DELAY = 3.5s,SHORT_DELAY = 2s
,延迟消息发送后就会通过cancelToastLocked
来隐藏Toast
,并从mToastQueue
中移除.
// 将Toast封装为ToastRecord添加到mToastQueue中
Toast#show -> NMS#enqueueToast -> mToastQueue#add(ToastRecord)
// mToastQueue对于非系统应用,最多同时50个ToastRecord,防止DOS
-> NMS#showNextToastLocked ->toastRecord.callBack#show
// 这里callback就是Toast#TN对象的远程Binder,最终回调TN中的方法运行在发起Toast的线程池中.
//显示后,延时关闭
scheduleTimeoutLocked(ToastRecord)->sendMessageDelayed
-> toastRecord.callBack#hide -> mHandler.post(mShow)
-> TN#handleHide -> mWm.removeView
//这里 callback 就是 Toast#TN 的远程Binder,运行在Binder线程池中,需要用Handler切换线程,最终通过 TN#handleHide 调用 WindowManager 移除 View
扩展阅读
浅析Android的窗口