【Android】Window和WindowManager

文章目录

    • 理解Window和WindowManager
      • Window和WindowManager
      • Window的内部机制
        • Window的添加过程
        • Window的删除过程
        • Window的更新过程
    • Window的创建过程
      • Activity的Window创建过程
      • Dialog的Window创建过程
      • Toast的Window创建过程

理解Window和WindowManager

Window是一个抽象类,它的具体实现是PhoneWindow。WindowManager是外界访问Window的入口,Window的具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC过程。Android中所有的视图都是通过Window来呈现的,不管是Activity、Dialog还是Toast,它们的视图实际上都是附加在Window上的,因此Window实际是View的直接管理者。

Window和WindowManager

为了分析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);//0,0 分别是type和flags参数
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);

Flags参数表示Window的属性,以下列举常用的选项:

  • FLAG_NOT_FOCUSABLE:表示Window不需要获取焦点,也不需要接收各种输入事件,此标记会同时启动FLAG_NOT_TOUCH_MODEL,最终事件会传递给下层的具有焦点的Window
  • FLAG_NOT_TOUCH_MODAL:在此模式下,系统会将当前Window区域以外的单击事件传递给底层的Window,当前Window区域以内的单击事件则自己处理。这个标记很重要,一般来说都需要开启此标记,否则其他Window将无法收到单击事件。
  • FLAG_SHOW_WHEN_LOCKED:开启此模式可以让显示在锁屏的界面

Type参数表示Window的类型,Window有三种类型,分别是应用Window子Window系统Window

  • 应用类Window对应着一个Activity。
  • 子Window不能单独存在,它需要附属在特定的父Window之中,比如常见的一些Dialog就是一个子Window。
  • 系统Window是需要声明权限才能创建的Window,比如Toast和系统状态栏这些都是系统Window。

Window是分层的,每个Window都有对应的z-ordered,层级最大的会覆盖在层级小的Window上面,这和HTML中的z-index的概念是完全一致的。在三类Window中,应用Window的层级范围是199,子Window的层级范围是10001999,系统Window的层级范围是2000~2999,这些层级属性范围对应着WindowManager.LayoutParamstype参数。

如果采用TYPE_SYSTEM_ERROR,只需要为type参数指定这个层级即可:

mLayoutParams.type = LayoutParams.TYPE_SYSTEM_ERROR

同时声明权限:<uses-permissionandroid:name="android.permission.SYSTEM_ALERT_WINDOW" />

WindowManager所提供的功能很简单,常用的只有三个方法,即添加View更新View删除View,这三个方法定义在ViewManager中,而WindowManager继承了ViewManager。

WindowManager操作Window的过程更像是在操作Window中的View

Window的内部机制

Window是一个抽象的概念,并不是实际存在的,它是以View的形式存在,每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系。在实际使用中无法直接访问Window,对Window的访问必须通过WindowManager。

Window的添加过程

Window的添加过程需要通过WindowManageraddView()来实现,WindowManager是一个接口,它的真正实现是WindowManagerImpl类。WindowManager的实现类对于addView()updateView()removeView()方法都是委托给WindowManagerGlobal类。

WindowManagerGlobal的addView()方法分为如下几步:

  1. 检查参数是否合法,如果是子Window那么还需要调整一些布局参数
  2. 创建ViewRootImpl并将View添加到列表中。Window对应的View, ViewRootImpl和待删除的View对象都有对应的列表。
  3. 通过ViewRootImplsetView()来更新界面(View的绘制由ViewRootImpl完成),setView()中通过requestLayout()完成异步刷新请求
  4. 通过WindowSession来添加Window,添加过程的本质是一个IPC过程,其中用到了Binder对象IWindowSession(实现类Session),实际添加是交给WindowManagerService去处理
Window的删除过程

和添加过程一样,都是先通过WindowManagerImpl后,再进一步通过WindowManagerGlobal来实现的↓

调用removeView(),其中先找到待删除的View索引,然后调用removeViewLocked()->ViewRootImpl,有同步删除和异步删除,在异步删除中,就会发送一个信息,放进刚刚ViewRootImpl中的待删除View的列表。

真正删除View的逻辑在dispatchDetachedFromWindow()方法的内部实现。主要做四件事:

  1. 垃圾回收的工作,比如清除数据和消息,移除回调。
  2. 通过Session的remove方法删除Window,mWindowSession.remove(mWindow),这同样是一个IP C过程,最终会调用WindowManagerService的removeWindow方法
  3. 调用View的dispatchDetachedFromWindow方法,在内部调用View的onDetachedFromWindow()以及onDetachedFromWindowInternal()。
  4. 调用WindowManagerGlobal的doRemoveView方法刷新数据,包括mRoots、mParams以及mDyingViews,需要将当前Window所关联的这三类对象从列表中删除。
Window的更新过程

调用WindowManagerGlobal 的updateViewLayout():

首先需要更新View的LayoutParams并替换掉老的LayoutParams,接着再更新ViewRootImpl中的LayoutParams,这一步是通过ViewRootImpl的setLayoutParams()方法来实现的。在ViewRootImpl中会通过scheduleTrversals方法来对View重新布局,包括测量、布局、重绘三个过程。除了View本身的重绘以外,ViewRootImpl还会通过WindowSession来更新Window的视图,这个过程最终是由WindowManagerService的relayoutWindow()来具体实现的,同样是一个IPC过程。

Window的创建过程

Activity的Window创建过程

1、Activity的启动过程很复杂,最终会由ActivityThread中的performLaunchActivity()来完成整个启动过程,在这个方法内部会通过类加载器创建Activity的实例对象,并调用其attach()方法为其关联运行过程中所依赖的一系列上下文环境变量。

在attach()中,系统会创建所属的Window对象并设置回调接口。

Window对象的创建是通过PolicyManagermakeNewWindow() 实现

2、Activity实现了Window的Callback接口,当Window接收到外界的状态变化时就会调用Activity的方法,例如onAttachedToWindow()onDetachedFromWindow()dispatchTouchEvent()等。

3、Activity的Window是由PolicyManager来创建的 - > 真正实现是Policy类,它会新建一个PhoneWindow对象,Activity的setContentView()的实现是由PhoneWindow来实现的/

PhoneWindowsetContentView()方法大致遵循如下几个步骤:

  1. 如果没有DecorView(FrameLayout,顶级View,包含内容和标题栏),那么就创建它,通过gernerateLayout()加载具体的布局文件。
  2. 将View添加到DecorView的mContentParent中,
  3. 回调Activity的onCreateChanged()方法通知Activity视图已经发生改变

Window更多表示的是一种抽象的功能集合…

Dialog的Window创建过程

Dialog的Window的创建过程和Activity类似,有如下步骤:

  1. 创建Window:Diolog中Window的创建同样是通过PolicyManager的makeNewWindow()方法来完成的,创建后的对象实际上就是PhoneWindow。
  2. 初始化DecorView并将Dialog的视图添加到DecorView中,与Activity类似。
  3. 调用Dialog的show(),将DecorView添加到Window中并显示, 也与Activity类似。
  4. 普通的Dialog有一个特殊之处,就是必须采用Activity的Context,如果采用Application的Context,那么就会报错 -> 应用token只有Activity拥有,所以这里只需要Activity作为Context来显示对话框即可。

系统Window比较特殊,不需要token,系统Window的层级范围type: 2000~2999,可以指定Dialog的Window类型为系统Window。

dialog.getWindow().setType(LayoutParams.TYPE_SYSTEM_ERROR);
//要声明权限

Toast的Window创建过程

  • 在Toast的内部有两类IPC过程,第一类是Toast访问NotificationManagerService,第二类是NotificationManagerService回调Toast里的TN接口。
  • Toast属于系统Window,它内部的视图由两种方式指定:一种是系统默认的演示,另一种是通过setView方法来指定一个自定义的View
  • Toast具有定时取消功能,所以系统采用了Handler。
  • Toast的显示show()和隐藏cancel()是IPC过程,都需要NotificationManagerService(NMS)来实现,在Toast和NMS进行IPC过程时,NMS会跨进程回调Toast中的TN类中的方法,TN类是一个Binder类,运行在Binder线程池中,所以需要通过Handler将其切换到当前发送Toast请求所在的线程,因为使用了Handler,所以Toast无法在没有Looper的线程中弹出。

对于非系统应用来说,mToastQueue最多能同时存在50个ToastRecord(应用的mToastQueue队列最多只能存在50个),这样做是为了防止DOS(Denial of Service,拒绝服务)。因为如果某个应用弹出太多的Toast会导致其他应用没有机会弹出Toast。

  • Toast的显示是通过ToastRecord的callback来完成,callback -> Tn对象的远程Binder,需要跨进程,会运行在Toast的应用的Binder线程池中。

  • 并且会发送一个延时消息,时长取决于Toast的持续时长,用来调用cancelToastLocked来隐藏Toast,并且从队列中移除,然后继续显示队列中的Toast

Toast的显示和隐藏实际上通过Toast的TN类中的两个Runnable --> handleShow()handleHide() 方法,用于将Toast的视图从Window添加或者移除。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/191979.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python逐步打造惊艳的折线图

大家好&#xff0c;Matplotlib可以快速轻松地使用现成的函数绘制图表&#xff0c;但是微调步骤需要花费更多精力。今天本文将介绍如何使用Matplotlib绘制吸引人的图表&#xff0c;实现折线图的惊艳变身。 1.数据 为了说明方法&#xff0c;本文使用了包含过去50年各国GDP信息的…

[iOS]常用修饰符详解

一、内存管理修饰符 这些修饰符主要用于管理对象的内存&#xff0c;包括引用计数和生命周期。 1.strong strong是一个引用计数修饰符&#xff0c;用于对象的属性&#xff0c;这是默认的属性修饰符。当你将一个对象赋值给一个strong属性时&#xff0c;该对象的引用计数会增加…

3090微调多模态模型Qwen-VL踩坑

本人使用记录一下训练过程中的心得和bug 1.数据集准备 数据集的标签形式见官方readme,如下: [{"id": "identity_0","conversations": [{"from": "user","value": "你好"},{"from": "…

如何理解函数指针

如何理解函数类型C 定义函数类型定义一个函数指针类型直接调用函数指针 定义函数类型 typedef int (MyFunction)(int a, int b);可以理解成定义了一个具有2个int类型的传入参数以及返回一个int类型的变量的函数类型>这只是对于函数类型的定义 如果要声明一个函数指针我们需…

QT 中 QTimer 类 备查

基础 // 指定了父对象, 创建的堆内存可以自动析构 QTimer::QTimer(QObject *parent nullptr);// 根据指定的时间间隔启动或者重启定时器, 需要调用 setInterval() 设置时间间隔 void QTimer::start();// 启动或重新启动定时器&#xff0c;超时间隔为msec毫秒。 void QTimer::…

韩语语法中에和로/으로区别,柯桥发音入门韩语培训学校

에和로/으로在行动的去向与到达或涉及的地点一致时&#xff0c;二者可以互换。 但是에表示到达或涉及的具体地点&#xff0c;而로/으로表示的时动作指向的方向或经过的地点。 在只表示去向而不表示具体地点时&#xff0c;只能用로/으로&#xff0c;而在只表示具体地点而不表示方…

2023.12.2 做一个后台管理网页(左侧边栏实现手风琴和隐藏/出现效果)

2023.12.2 做一个后台管理网页&#xff08;左侧边栏实现手风琴和隐藏/出现效果&#xff09; 网页源码见附件&#xff0c;比较简单&#xff0c;之前用很多种方法实现过该效果&#xff0c;这次的效果相对更好。 实现功能&#xff1a; &#xff08;1&#xff09;实现左侧边栏的手…

摩根士丹利:人工智能推动增长

摩根士丹利&#xff08;NYSE&#xff1a;MS&#xff09;将人工智能战略整合到其财富管理业务中&#xff0c;标志着竞争性金融格局迈出了变革性的一步。该公司的人工智能计划&#xff0c;包括与 OpenAI 合作开发人工智能聊天机器人&#xff0c;促进了其财富部门的显着增长。值得…

MuleSoft 中的细粒度与粗粒度 API

API 设计是一个令人着迷的话题。API 设计的一个重要方面是根据 API 的特性和功能确定正确的“大小”。所有建筑师都必须在某个时候解决过这个问题。在本文中&#xff0c;我将尝试对我们在获得“正确的”粒度 API 之前需要考虑的各种参数进行一些深入的探讨&#xff1a; 可维护…

Doris 数据导入四:Binlog Load 方式

Binlog Load提供了一种使Doris增量同步用户在Mysql数据库的对数据更新操作的CDC(Change Data Capture)功能。 1 适用场景 INSERT/UPDATE/DELETE支持。 过滤Query。 暂不兼容DDL语句。 2 基本原理 在第一期的设计中,Binlog Load需要依赖canal作为中间媒介,让canal伪造成一个…

【数据库】数据库基于封锁机制的调度器,使冲突可串行化,保障事务和调度一致性

封锁使可串行化 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会定期更…

Linux查看计算机处理器相关的信息

采用命令lscpu。部分结果如下&#xff1a;

MicroPython标准库

MicroPython标准库 arraybinascii(二进制/ASCII转换)builtins – 内置函数和异常cmath – 复数的数学函数collections – 集合和容器类型errno – 系统错误代码gc – 控制垃圾收集器hashlib – 散列算法heapq – 堆队列算法io – 输入/输出流json – JSON 编码和解码math – 数…

详解Spring中BeanPostProcessor在Spring工厂和Aop发挥的作用

&#x1f609;&#x1f609; 学习交流群&#xff1a; ✅✅1&#xff1a;这是孙哥suns给大家的福利&#xff01; ✨✨2&#xff1a;我们免费分享Netty、Dubbo、k8s、Mybatis、Spring...应用和源码级别的视频资料 &#x1f96d;&#x1f96d;3&#xff1a;QQ群&#xff1a;583783…

【开源】基于Vue+SpringBoot的音乐平台

项目编号&#xff1a; S 055 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S055&#xff0c;文末获取源码。} 项目编号&#xff1a;S055&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示 四、核心代码4.1 查询单首…

Vue安装及环境配置详细教程

一、下载node.js 访问node.js官网&#xff1a;Download | Node.js 选择Windows Installer (.msi)的64-bit进行下载。 在E盘新建一个文件夹&#xff0c;取名为nodejs&#xff0c;也可以在其他盘符新建。 在安装node.js时&#xff0c;点击Change...&#xff0c;进行切换盘符安…

使用 STM32 微控制器读取光电传感器数据的实现方法

本文介绍了如何使用 STM32 微控制器读取光电传感器数据的实现方法。通过配置和使用STM32的GPIO和ADC功能&#xff0c;可以实时读取光电传感器的模拟信号并进行数字化处理。本文将介绍硬件连接和配置&#xff0c;以及示例代码&#xff0c;帮助开发者完成光电传感器数据的读取。 …

<JavaEE> 什么是线程安全?产生线程不安全的原因和处理方式

目录 一、线程安全的概念 二、线程不安全经典示例 三、线程不安全的原因和处理方式 3.1 线程的随机调度和抢占式执行 3.2 修改共享数据 3.3 关键代码或指令不是“原子”的 3.4 内存可见性和指令重排序 四、Java标准库自带的线程安全类 一、线程安全的概念 线程安全是指…

vue3高雅的使用useDialog

在Vue 3中&#xff0c;我们可以使用useDialog自定义指令来高雅地实现对话框的功能。以下是一个简单的示例&#xff1a; 首先&#xff0c;我们需要创建一个名为useDialog.js的文件&#xff0c;并在其中定义我们的自定义指令&#xff1a; // useDialog.js import { ref } from …

无人机助力电力设备螺母缺销智能检测识别,python基于YOLOv7开发构建电力设备螺母缺销小目标检测识别系统

传统作业场景下电力设备的运维和维护都是人工来完成的&#xff0c;随着现代技术科技手段的不断发展&#xff0c;基于无人机航拍飞行的自动智能化电力设备问题检测成为了一种可行的手段&#xff0c;本文的核心内容就是基于YOLOv7来开发构建电力设备螺母缺销检测识别系统&#xf…