这是我在学习过程中总结的知识
目的是希望日后回来看或者需要用的时候可以 一目了然 # 的回顾、巩固、查缺补漏
不追求详细相当于书本的精简版或者说是导读(想看详细的直接对应翻书),但会尽力保证读者都能快速理解和快速使用(随理解加深会总结的更加精简),但必要时会附上一些较详细解释的链接
脚注是空白的:表示还没弄懂的知识,了解后会添加
- 常用Window来实现悬浮窗,它还是View的直接管理者
- Window是抽象类,具体实现为PhoneWindow
- WindowManager 是外界访问Window的入口
- WindowManagerService 是Window的具体实现
- WindowManager和WindowManagerService交互是一个IPC过程
- Android所有视图都是附在Window上的(Activity、Toast等)
8.1 Window和WindowManager
先通过代码演示通过WindowManager添加Window的过程
总体分两部分看,一是生成一个View(Button),然后设置View的参数(LayoutParams)
mFloatingButton = new Button(this);
mFloatingButton.setText("click me");
mLayoutParams = new WindowManager.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,
PixelFormat.TRANSPARENT);
mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
| LayoutParams.FLAG_NOT_FOCUSABLE
| LayoutParams.FLAG_SHOW_WHEN_LOCKED;
mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;
mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
mLayoutParams.x = 100;
mLayoutParams.y = 300;
mFloatingButton.setOnTouchListener(this);
mWindowManager.addView(mFloatingButton, mLayoutParams);
然后LayoutParams中的flags和type是比较重要的参数,下面介绍一些常用的
FLAG_NOT_FOCUSABLE
表示Window不需要获取焦点,然后同时启动FLAG_NOT_TOUCH_MODAL,事件还会传递给下层具有焦点的Window
FLAG_NOT_TOUCH_MODAL
简单来说,悬浮窗内的点击事件自己处理,而不接收悬浮窗外的点击事件
FLAG_SHOW_WHEN_LOCKED
将Window显示在锁屏界面上
Type参数表示Window的类型
- 应用Window(1~99):需要和Activity一起
- 子Window(1000~1999):不能单独,需要附属在父Window中,如Dialog
- 系统Window(2000~2999):需要先声明权限才能创建,如Toast和系统状态栏
Window是分层的,每个Window都有对应的z-ordered(如上的数字)
层级大的会覆盖层级小的Window上面
使用系统Window需要声明权限 uses-permission android:name=“android.permission.SYSTEM_ALERT_WINDOW”
WindowManager常用的3个功能,添加、更新、删除View(方法继承于ViewManager接口)
8.2 Window的内部机制
Window是一个抽象的概念,每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系,因此Window并不是实际存在的而是以View的形式存在
从WindowManager的定义也可以看出,它的操作都是针对View的,要访问Window必须通过WindowManager
8.2.1 Window 的添加过程
WindowManager是一个接口,真正实现是 WindowManagerImpl 继承的addView方法
查看源码可知,WindowManagerImpl将操作又交给了 WindowManagerGlobal 来处理,这是一种桥接的工作模式
现在来看WindowManagerGlobal的addView 方法
1.检查参数合法性,可能调整子Window参数
2.创建一个ViewRootImpl然后将之前所有的View、新的ViewRootImpl、之前所有的布局参数添加到对应的list中
3. 通过ViewRootImpl来更新界面并完成Window的添加过程
8.2.2 Window的删除过程
8.2.3Window的更新过程
8.3 Window的创建过程
View是Android中的视图呈现方式,有视图的地方就有Window
8.3.1 Activity的Window创建过程
· 在Activity的attach方法里,系统会创建Activity所属的Window对象并为其设置回调接口
· Window对象的创建时通过PolicyManager的makeNewWindow方法实现的
· 由于Activity实现了Window的Callback接口,所有当Window接收到外界状态改变时就会回调Activity的方法
PolicyManager
Activity的Window是通过PolicyManager的一个工厂方法来创建的,这些方法全部在策略接口IPolicy中声明了
Policy类
PolicyManager的真正实现是Policy类,关键的 makeNewWindow 方法在Policy内
PhoneWindow
makeNewWindow将Window的具体实现推给PhoneWindow
PhoneWindow的setContentView
- 如果没有DecorView就创建它
DecorView是一个FrameLayout、是Activity的顶级View
创建它的方法由installDecor——generateDecor完成
再通过generateLayout初始化DecorView的结构加载布局文件
-
将View添加到DecorView的mContentParent中
这一步直接将Activity的视图添加到DecorView的mContentParent中即可,然后Activity的布局文件已经添加到DecorView里面了 -
回调Activity的 onContentChanged 方法通知Activity视图已经发生改变
之前说的Activity实现了Window的Callback接口,所以现在要通知Activity作出相应的处理(onContentChanged)
最后还需要让WindowManager识别DecorView,再通过Manager添加到Window才能实现接收外界信息输入的功能
8.3.2 Dialog的Window过程
1. 创建Window
和Activity类似,也是通过PolicyManager的makeNewWindow方法来完成的,创建后的对象是PhoneWindow
2. 初始化DecorView并将Dialog的视图添加到DecorView中
也和Activity类似,都是通过Window去添加指定的布局文件
3. 将DecorView添加到Window中并显示
在Dialog的show 方法中,会通过WindowManager将DecorView添加到Window中,也类似··
普通的Dialog必须采用Activity的Context不能是Application的Context
8.3.3 Toast的Window创建过程
- Toast采用了Handler来实现定时取消
- 视图有两种:一是默认样式、二是通过setView指定一个自定义View.它们都对应Toast的一个View类型的内部成员(mNextView)
显示和隐藏Toast都需要NMS(NotificationManagerService)来实现,由于NMS运行在系统进程,所以需要用到TN这个类(Binder类).
即:Toast和NMS进行IPC过程中,NMS处理Toast的请求会跨进程回调TN中的方法
Toast的显示过程
- 调用了NMS的enqueueToast方法
- enqueueToast将Toast请求封装为ToastRecord对象并添加到一个名为mToastQueue的队列中(ArrayList)
- 显示时由ToastRecord的callback(TN对象的远程Binder)来完成的
- Toast显示以后,NMS通过scheduleTimeoutLocked方法来发送一个延时消息
- 延时结束后,MNS通过cancelToastLocked方法来隐藏Toast并将其从mToastQueue中移除
由第三点可知,Toast真正的显示隐藏方法是由TN完成的
即TN中的handleShow和handleHide