前言

本文已经收录到我的Github个人博客,欢迎大佬们光临寒舍:

我的GIthub博客

本文已授权公众号郭霖秦子帅

学习清单:

  • Window&WindowManagerService

  • Window&WindowManager

  • Window&PhoneWindow

  • Window&Activity

  • Window&View

  • Window内部机制

  • Window创建过程

一.为什么要学习Window?

Android手机上所有的视图都是通过Window来呈现的,像常用的ActivityDialogPopupWindowToast,他们的视图都是附加在Window上的,所以可以这么说 ——「Window是View的直接管理者」。

Window是一个顶层窗口查看和行为的一个抽象基类,这个类的实例作为一个顶级View添加到Window Manager。它提供了一套标准的UI方法,比如添加背景,标题等等。

Window本身很抽象,深入了解Window,不仅有助于你了解Android系统中各个层级之间的关系,还可以对Toast的内部机制、自定义等等方面会有更加深入的体会。

注意:StatusBar也包含在DecorView之内

二.核心知识点归纳

2.1 Window关系解析

看到下面这张大图,是不是感觉有点乱乱的,别急,别急,心急吃不了热豆腐,笔者将向您娓娓道来

2.1.1 Window&PhoneWindow

笔者之前在进阶之路 | 奇妙的View之旅中,提及setContentView的时候简单说到了WindowPhoneWindow,相信看过的读者已经对此有一个简单的印象。

Window是一个抽象类,它定义了顶级窗体样式和行为。其唯一的实现类PhoneWindow

2.1.2 Window&View

笔者之前在进阶之路 | 奇妙的View之旅中,提及View工作流程的时候简单说到了ViewRootImpl,相信看过的读者已经对此有一个简单的印象。

每个Window都对应一个View和一个ViewRootImplWindowView通过ViewRootImpl来建立联系。Window不可见,它实际以View的形式存在,它是View的直接管理者

2.1.3 Window&WindowManagerService

想了解IPC的读者,可以看下笔者写的一篇博客: 进阶之路 | 奇妙的 IPC 之旅

Window具体实现位于WindowManagerService中。WindowManagerWindowManagerService的交互是一个IPC(跨进程通信)过程。

2.1.4 Window&WindowManager

实际使用中无法访问Window,对Window的访问必须通过WindowManager(换句话说,WindowManager是外界访问Window的入口),对Window的操作通过它完成。

  • 例如:通过WindowManager添加Window
//将一个Button添加到屏幕为(100,300)的位置
mFloatingButton = new Button(this);
mFloatingButton.setText("test button"); mLayoutParams = new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,PixelFormat.TRANSPARENT);//第三个参数代表flags,第四个参数代表type mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
| LayoutParams.FLAG_NOT_FOCUSABLE
| LayoutParams.FLAG_SHOW_WHEN_LOCKED;//配置flags
mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR;//配置type
mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;//配置gravity
mLayoutParams.x = 100;//相对于gravity
mLayoutParams.y = 300;//相对于gravity mFloatingButton.setOnTouchListener(this);
mWindowManager.addView(mFloatingButton, mLayoutParams);

下面依次介绍WindowManager的三个重要参数:

  • flags:表示Window的属性。主要的可选值含义:
  • FLAG_NOT_FOCUSABLE:表示Window不需要获取焦点,也不需要接收各种输入事件,此标记会同时启动FLAG_NOT_TOUCH_MODEL,最终事件会传递给下层的具有焦点的Window
  • FLAG_NOT_TOUCH_MODAL:表示系统会将当前Window区域以外的单击事件传递给底层的Window,而区域以内的单击事件则自己处理。一般都需要开启此标记,否则其他Window将无法收到单击事件
  • FLAG_SHOW_WHEN_LOCKED:表示Window可显示在锁屏界面
  • type:表示Window的类型。

    Window有三种类型:

    A.应用类Window:对应一个Activity或者Dialog

    B.Window:不能单独存在,需附属特定的父Window。如PopupWindow,ContextMenu,OptionMenu

    注意:ContextMenu,OptionMenuDialog的子类,它们修改了自身的窗口类型

    C.系统Window: 需声明权限才能创建。如Toast

    • 系统权限有很多值,一般选用:TYPE_SYSTEM_OVERLAY/TYPE_SYSTEM_ERROR

    • 记得声明权限:< uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>,Android6.0以下直接声明权限即可,Android6.0以上还需要用户打开软件设置页手动打开,才能授权。

  • Window是分层的,见下表
  • 层级大的会覆盖在层级小的Window上面。
  • 对应WindowManager.LayoutParamstype参数。
Window 层级
应用Window 1-99
Window 1000-1999
系统Window 2000-2999
  • gravity:表示Window的位置。
  • 默认是屏幕中间
  • xy值相对于gravity

2.2 Window的内部机制

  • WindowManagerWindow主要有三大操作:添加、更新和删除

    这三个方法主要是定义在ViewManager接口中:

public interface ViewManager
{
public void addView(View view, ViewGroup.LayoutParams params);//添加过程
public void updateViewLayout(View view, ViewGroup.LayoutParams params);//更新过程
public void removeView(View view);//删除过程
}
  • WindowManager也是一个接口,它继承了ViewManager接口:
public interface WindowManager extends ViewManager {}
  • WindowManager的具体实现类是WindowManagerImpl
public final class WindowManagerImpl implements WindowManager{
@Override
public void addView(View view, ViewGroup.LayoutParams params){
mGlobal.addView(view, params, mDisplay, mParentWindow);
} @Override
public void updateViewLayout(View view, ViewGroup.LayoutParams params){
mGlobal.updateViewLayout(view, params);
} @Override
public void removeView(View view){
mGlobal.removeView(view, false);
}
}
  • 由以上代码可见,WindowManagerImpl并没有直接实现Window的三大操作,而是交给了WindowManagerGlobalWindowManagerGlobal单例模式向外提供自己的实例:

WindowManagerImpl这种工作模式是典型的桥接模式

private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();

一幅图说明这几个类的关系:

因此,通过WindowManagerGlobaladdView()updateViewLayout()removeView()实现WindowManagerWindow的添加、删除和修改。

2.2.1 Window的添加

2.2.2 Window的删除

2.2.3 Window的更新

不难发现,以上验证了之前的总结:

  • Window的三大操作最终都会通过一个IPC过程移交给WindowManagerService
  • WindowView通过ViewRootImpl来联系,ViewRootImpl可控制View的测量、布局和重绘。

限于篇幅,笔者这里暂未贴上源码,如果想了解的话,推荐一篇文章:我眼中的Window创建/添加/删除/更新过程

2.3 Window的创建过程

由于View必须依附Window才能呈现出来,因此有View的地方必有Window。在Android中可以提供View的地方有ActivityDialogToastPopupWindow,菜单,下面分别来看ActivityDialogToast三种Window的大致创建过程

2.3.1 ActivityWindow创建过程

想详细了解Activity的启动流程的,推荐一篇笔者写的文章:进阶之路 | 奇妙的四大组件之旅

想了解ActivityWindow创建过程的源码的读者,笔者推荐一篇文章: Activity的Window创建过程分析

2.3.2 DialogWindow创建过程

  • Dialog.show():完成DecorView的显示
  • WindowManager.remoteViewImmediate()方法:当Dialogdismiss时移除DecorView

2.3.3 ToastWindow创建过程

Q1:Toast的内部的视图由两种方式指定:

  • 系统默认的样式
  • 通过setView()指定一个自定义View

Q2:Toast具有定时取消功能,故系统采用Handler做定时处理

Q3:在Toast内部有两类IPC过程:

  • Toast访问NotificationManagerService(NotificationManagerService运行在系统的进程);
  • NotificationManagerService回调Toast里的TN接口(运行在Binder线程池)。

Q4:Toast提供方法show()cancel()分别用于显示和隐藏Toast

  • Toast的显示和隐藏都需要通过NMS来实现,由于NMS运行在系统进程中,故需通过远程调用的方式来进行显示和隐藏Toast。
  • NMS处理Toast的显示和隐藏请求时会跨进程回调TN中的方法,但是由于TN运行在Binder线程池中,故需通过Handler将其切换到当前线程(发送Toast请求的线程)。
  • NMS只是起到了管理Toast队列及其延时的效果
  • Toast 的显示和隐藏实际是通过TN来实现的。

想了解ToastWindow创建过程的源码的读者,笔者推荐一篇文章:Android对话框Dialog,PopupWindow,Toast的实现机制

三.课堂小测试

恭喜你!已经看完了前面的文章,相信你对Window已经有一定深度的了解,下面,进行一下课堂小测试,验证一下自己的学习成果吧!

Q1:一个应用中有多少个Window?

答案:无限个。原因:任何一个View都是依附在Window上面,一个应用可以有无限个View,自然Window也是无限个。

Q2:Window对象有存在的必要吗

  • 疑惑点:Window能做的事情,View对象基本都能做:像触摸事件、管理各个子View等等。
  • 可能有人会说:“WindowView的管理者。”
  • 追问:我们知道,WindowManagerWindow的管理者,那为什么不直接用WindowManager管理View呢?
  • 答案:站在系统的角度上看,系统是“不知道”有View对象这个说法的!作为系统,我有自己的骄傲,不去管你Window如何搬砖、如何砌墙,只给你地皮。而这时,Window为了绘制出用户想要的组件(按钮、文字、输入框等等),系统又不给我!没事,那我自己定义,于是就定义了View机制,给每个View提供Canvas,让不同的View自己绘制具有自己特色的组件。同时,为了更好的管理View,通过定义ViewGroup,等等。

Q3:Activity有存在的必要吗?

  • 疑惑点:Window已经是系统管理的窗口界面。那么为什么还需要Activity呢?我们把Activity所做的事情,全部封装到Window不就好了?悬浮窗口Dialog中不就是没有使用Activity来显示一个悬浮窗吗?
  • 答案:Android中的应用中,里面对各个窗口的管理相当复杂(任务栈、状态等等)。但是如果让用户自己去管理这些Window,先不说工作量,光让用户自己去实现任务栈这点,就很难了。为了让大家能简单、快速的开发应用,AndroidActivity帮我们管理好,我们只需简单的去重写几个回调函数,无需直接与Window对象接触。

任何事物都有规律,语言再难,也是人发明的,一样具有社会性,其实这几个的关系就像是国家的中央系统的官员分配一样,从古至今,一层对一层负责,这样各司其职,又相互一层层联系着,达到效率最大化,突然发现,古人的智慧还是很厉害的,你让皇帝(系统)去管辖所有的官员(view),岂不是要累死?所以才出现了中间这些官员(window


如果文章对您有一点帮助的话,希望您能点一下赞,您的点赞,是我前进的动力

本文参考链接:

最新文章

  1. Codeforces Round #379 (Div. 2) E. Anton and Tree 树的直径
  2. HDU 4639 Hehe(字符串处理,斐波纳契数列,找规律)
  3. 为什么要使用jQuery?
  4. gnome中文翻译之po
  5. CentOS 6.7编译安装MySQL 5.6
  6. Cocos2d-x学习笔记(9)(CCTextFieldTTF使用输入框)
  7. Intellij IDEA下导出Java工程的可运行JAR包
  8. c/c++常用的几个关键字总结
  9. YUI3组件框架之plugin
  10. AJAX跨域问题解决方法(3)——被调用方解决跨域
  11. Citrix 桌面虚拟化解决方案与VMware桌面虚拟化解决方案对比
  12. SpringIOC原理简述
  13. Spring基础知识备案
  14. 一般处理程序(ashx)的使用
  15. Hashtable与ConcurrentHashMap区别(转)
  16. 【python练习题】程序12
  17. Allegro PCB Design GXL (legacy) 将指定的层导出为DXF
  18. 010.Docker Compose构建WordPress实战
  19. zabbix agent配置详解(windows)
  20. PuTTY+Xming实现X11的ssh转发

热门文章

  1. JVM内存布局及GC知识
  2. EditPlus 添加 打开文件所在文件夹 功能
  3. laravel 事件机制 实践总结
  4. Git详解之初次运行
  5. docker启动redis端口映射错误问题解决
  6. idea|properties文件乱码
  7. CSS学习笔记--Div+Css布局(div+span以及盒模型)
  8. React脚手架解决不能使用less问题
  9. Struts(六)
  10. OpenCV3入门(三)基本绘图函数