WindowManager的addView是定义在接口ViewManager,同时WindowManager也是一个接口,通过Ctrl+H 看一下继承关机,发现是在WindowManagerImpl继承了WindowManager接口并实现addView方法
WindowManagerImpl.addView
mGlobal是WindowManagerGlobal的实例,所以调用的的是WindowManagerGlobal.addView()
代码中创建了一个ViewRootImpl实例root,并且调用root.setView传入view
在ViewRootImpl.setView里最关键的代码是
mWindowSession的类型是IWindowSession,mWindow的类型是IWindow.Stub, 这句代码就是利用AIDL进行IPC, 实际被调用的是Session.addToDisplay:
mService是WindowManagerService,继续往下看
mPolicy是标记为final的成员变量:
final WindowManagerPolicy mPolicy =PolicyManager.makeNewWindowManager();
继续看PolicyManager.makeNewWindowManager,实际是Policy.makeNewWindowManager()
现在我们知道mPolicy实际上是PhoneWindowManager, 那么
intres =mPolicy.checkAddPermission(attrs, appOp);
实际调用的代码是PhoneWindowManager.checkAddPermission()
TYPE_TOAST不检查permission
TYPE_PHONE检查SYSTEM_ALERT_WINDOW权限
TYPE_TOAST不需要权限显示悬浮窗的原因已经找到了,接着刚才addWindow方法的分析, 继续看下面一句:
还是在PhoneWindowManager里的方法adjustWindowParamsLw()
注意这里我给出了三个版本的实现, 一个是2.0到2.3.7实现的版本, 一个是4.0.1到4.3.1实现的版本, 一个是4.4实现的版本:
//Android 2.0 - 2.3.7 PhoneWindowManager public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) { switch (attrs.type) { case TYPE_SYSTEM_OVERLAY: case TYPE_SECURE_SYSTEM_OVERLAY: case TYPE_TOAST: // These types of windows can't receive input events. attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; break; } } //Android 4.0.1 - 4.3.1 PhoneWindowManager public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) { switch (attrs.type) { case TYPE_SYSTEM_OVERLAY: case TYPE_SECURE_SYSTEM_OVERLAY: case TYPE_TOAST: // These types of windows can't receive input events. attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; break; } } //Android 4.4 PhoneWindowManager @Override public void adjustWindowParamsLw(WindowManager.LayoutParams attrs) { switch (attrs.type) { case TYPE_SYSTEM_OVERLAY: case TYPE_SECURE_SYSTEM_OVERLAY: // These types of windows can't receive input events. attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; attrs.flags &= ~WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; break; } } |
在4.0.1以前, 当我们使用TYPE_TOAST, Android会偷偷给我们加上FLAG_NOT_FOCUSABLE和FLAG_NOT_TOUCHABLE,4.0.1开始, 会额外再去掉FLAG_WATCH_OUTSIDE_TOUCH,这样真的是什么事件都没了. 而4.4开始, TYPE_TOAST被移除了, 所以从4.4开始, 使用TYPE_TOAST的同时还可以接收触摸事件和按键事件了, 而4.4以前只能显示出来, 不能交互.
API level 18及以下使用TYPE_TOAST无法接收触摸事件的原因也找到了.