0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

窗口子系统基本概念与流程分析

王程 来源:jf_75796907 作者:jf_75796907 2024-03-05 09:45 次阅读

窗口子系统位于 fundationwindowmanager 目录下,提供对窗口与 Display 管理的基础能力

概览

窗口是什么

每个 Ability 在创建时都会创建一个主窗口,并且为该窗口设置 ACE 中的 UIContent 用于加载展示 UI 界面。基本上所有的 UI 视图都是在窗口中展示的,比如弹窗、toast、系统状态栏导航栏、应用等。因此窗口子系统是系统图形界面显示所需的基础子系统。

窗口的种类

  • 主窗口: 应用显示的主窗口,即每个 Ability 持有的主窗口
  • 子窗口: 必须依附于主窗口来创建与显示
  • 系统窗口 :其他窗口均属于系统窗口

窗口的属性

WindowFlag
flag 指定窗口的部分测量规则:

WINDOW_FLAG_NEED_AVOID 是否避开区域,默认避开,比如状态栏导航栏区域

WINDOW_FLAG_PARENT_LIMIT 是否受到父窗口的限制,默认不限制,如果限制,则宽高不能超过父窗口,需与 WINDOW_MODE_FLOATING 配合使用
WindowMode
mode 指定窗口的布局规则:

WINDOW_MODE_UNDEFINED 默认模式,默认宽高为 display 宽高减去状态栏导航栏等的宽高

WINDOW_MODE_FULLSCREEN 全屏模式,但需要与 * * * WINDOW_FLAG_NEED_AVOID 一起使用,默认宽高为 display 宽高

WINDOW_MODE_SPLIT_PRIMARY 分屏主窗口模式,如果是横屏则位于左侧,竖屏位于上方

WINDOW_MODE_SPLIT_SECONDARY 分屏副窗口模式,如果是横屏则位于右侧,竖屏位于下方

WINDOW_MODE_FLOATING 悬浮模式,悬浮窗口可以通过窗口边缘改变窗口大小,默认宽高为 display 宽高的 3/4
应用主窗口可以通过启动 ability 时的参数 Want::PARAM_RESV_WINDOW_MODE(ohos.aafwk.param.windowMode)来侧面指定 WindowMode 的值

priority
窗口优先级决定了窗口的层级,priority 越大窗口越靠近顶部。该属性位于 WindowNode 内,且只能由 WindowType 决定。

WindowType
窗口类型的改变会引起 flag、mode、priority 或其他属性的改变,从而达到改变窗口的测量、排列与层级的目的。如:

  • WINDOW_TYPE_STATUS_BAR
property_->SetWindowMode(WindowMode::WINDOW_MODE_FLOATING);
property_->SetFocusable(false);

  • WINDOW_TYPE_KEYGUARD
RemoveWindowFlag(WindowFlag::WINDOW_FLAG_NEED_AVOID);
SetWindowMode(WindowMode::WINDOW_MODE_FULLSCREEN);
​

层级则是由 WindowType 的 Priority 值与类型共同决定,同类型取值越大层级越高,WindowType 的 Priority 定义位于 foundationwindowmanagerwmserverincludewindow_zorder_policy.h 中,如:

WINDOW_TYPE_WALLPAPER = 0

WINDOW_TYPE_DESKTOP = 1

WINDOW_TYPE_APP_MAIN_WINDOW = 0

WINDOW_TYPE_APP_SUB_WINDOW = 1

WINDOW_TYPE_STATUS_BAR = 110

WINDOW_TYPE_KEYGUARD = 114

WINDOW_TYPE_BOOT_ANIMATION = 117
WindowType 的类型则有三种:

BelowApp 位于底层,如桌面、壁纸等

App 位于中间,如应用主窗口、子窗口

AboveApp 位于上方,如锁屏、状态栏等

WindowType 是在这几个属性中,开发者目前唯一能直接修改的窗口属性:

window.setWindowType(type: WindowType): Promise< void >
​

Window、Display、Screen 的关系
Screen 是物理屏幕,Display 是逻辑屏幕,Window 则依附于 Display。Screen 与 Display 之间是多对多的关系,Display 与 Window 也是多对多的关系。在普通的单屏场景下,Screen 与 Display 是一对一,Display 与 Window 则是一对多。

WindowManagerService
WMS 主要负责 Window 的管理,比如创建、销毁、布局、层级的管理,并提供窗口布局、焦点、事件分发的能力,但不负责绘制。主要职责如下:

  • 管理 Window 的创建与销毁、窗口的属性的维护
  • 窗口树的维护
  • 窗口焦点的管理
  • 窗口的层级管理以及输入法窗口的层级提升
  • 窗口布局与策略的管理
  • 提供窗口的缩放与拖拽能力
    避开区域的管理
  • 加载 ACE 布局并触发布局回调事件

DisplayManagerService

DMS 提供 Display 信息、Display 事件通知以及管理 Display 与 Screen 映射关系,其他能力主要通过 RenderService 实现。主要职责如下:

  • 通过 RenderService 获取并管理 Screen
  • ScreenGroup 的管理
  • Display 的管理,以及其与 Screen 的映射管理
  • 对外提供显示信息,如宽高、虚拟像素比等
  • 提供截屏、量灭屏、横竖屏、亮度等屏幕相关能力
  • 提供扩展屏幕或镜像屏等多屏能力
  • 虚拟屏幕的管理
  • Display 事件的通知,如屏幕亮灭、显示大小、横竖屏、冻结等事件

窗口管理流程分析

创建窗口

窗口的创建从 Ability 的 OnStart 声明周期函数中触发。

  • Ability 持有 AbilityWindow,AbilityWindow 则持有 WindowScene
  • WindowScene 在初始化阶段会创建一个主窗口
  • 窗口的创建会调用 Window::Create 函数创建 WindowImpl 对象,并调用 WindowManagerService::CreateWindow 函数
  • 在 WindowManagerService 中,则通过 WindowController 生成 windowId 并创建 WindowNode
  • 最后通过 WindowRoot 将 WindowNode 管理起来

AbilityWindow 与 WindowScene 的关系

AbilityWindow 是 Ability 持有用来在生命周期函数中生成或调用窗口生命周期的类,操作窗口的类则是 WindowScene。WindowScene 由 WindowManager client 端提供,用于屏蔽元能力与窗口管理之间强耦合,方便后续无屏幕的小型设备裁剪显示系统。

WindowImpl 与 WindowNode 的区别

WindowImpl 是 IWindow 的实现,是提供给上层操作窗口的接口。WindowNode 与 WindowImpl 一一对应,是 WMS 中操作窗口的实体,其通过 WindowRoot 管理。* WindowNode 内部维护了一个 windowToken_对象,该对象的指向就是 WindowImpl。

  • WindowImpl 负责对应用于其他子模块提供操作窗口的能力,能力通过 WMS 与 RenderService 实现。WindowImpl 在创建时会创建 RSSurfaceNode 对象,该对象则会向 RenderService 提交一条窗口创建的事务。
  • 在 WindowNode 创建时,WindowImpl 会将 RSSurfaceNode 的引用传递给 WindowNode。
  • WindowNode 则是 WMS 中对窗口的抽象,内部维护了父子关系、显示隐藏、布局大小等。

WindowRoot 的作用

顾名思义,WindowRoot 管理着所有的窗口。其内部维护着 WindowNode 与 WindowId 的 map,提供了对 WindowNode 的增删改查操作,并且提供了最小化所有窗口、最大化窗口、设置布局策略等能力。

WindowImpl 的管理

主窗口的 WindowImpl 由 WindowScene 持有,子窗口则由主窗口自己管理维护。在 Ability 销毁时,会通知 WindowScene 销毁主窗口,主窗口则会销毁所有的子窗口,并通知 WMS 中的 WindowRoot 销毁相应的 WindowNode。

窗口的显示

创建的流程仅仅是创建了 WindowImpl 与 WindowNode,并未涉及布局与渲染,那么窗口是如何显示的呢?

  • 窗口的显示也是通过 Ability 触发,在其生命周期函数 OnActive/OnForground 内,会调用到 WindowScene::GoForeground 中。
  • 窗口的显示也可以通过在 ets 中手动调用 window.show()触发
  • 调用主窗口的 show 方法,即 WindowImpl::Show
  • 在其中会做一些判断,比如桌面的显示,会将其他 app 都最小化
  • 接着 WindowImpl 通过 WMS 调用 WindowRoot 的 AddWindowNode 函数,并将 windowId 传递过来
  • WindowRoot 通过 windowId 查找 WindowNode,并通过 diaplayId 创建或者获取 WindowNodeContainer 对象,并调用其 AddWindowNode 函数
  • 在 WindowNodeContainer 内,会判断 window 类型并将 window 加入到相应的父窗口中(appRoot、belowRoot、aboveRoot)
  • 接着会处理 WindowNode 中父子关系的映射,并调用 DMS 服务的 UpdateRSTree
  • 处理所有窗口的 z 值,并按规则设置到每个窗口的 surfaceNode 中,该操作会向 RenderService 提交一条事务。
  • z 值的规则为:从 belowRoot->appRoot->aboveRoot,z 值越来越大。同一类型中,window 的 priority 越大,z 值越大。同一 priority 的情况下,窗口被添加的越晚,z 值越大。z 值越大,排列越靠上。
  • WindowNodeContainer 维护着两种布局策略,CASCADE 与 TILE,在维护完 z 值与父子关系等操作后,会调用布局策略的 AddWindowNode 函数
  • 下面的流程均基于 CASCADE 策略
  • 判断窗口 Visibility,为 false 则不布局
  • 判断避开区域,限制窗口大小。如果是全屏窗口,则宽高与 display 一致。
  • 如果是悬浮窗口,默认大小设置为 display 的 3/4,并设置一个 Decorate 矩形,该矩形为窗口增加了 37vp、5vp、5vp、5vp(上右下左),该矩形用于拖拽与平移
  • 如果设置了 WINDOW_FLAG_PARENT_LIMIT 标记并且是子窗口,限制子窗口的大小不能超过父窗口
  • 为悬浮窗口设置 hotZone,上下左右均增加 20vp。该区域用于多模输入模块判断手指是否落在 window 内,也就是增加判断范围。
  • 调用窗口的 surfaceNode 的 SetBounds 函数,指定窗口的坐标与大小。该函数也会向 RenderService 提交一条事务。
  • 迭代子窗口,为其执行同样的流程
  • 总结下来,窗口的显示就是处理了父子关系、窗口先后关系,以及确定了坐标与大小,最后向 RenderService 提交事务,等待下个 vsync 的绘制

WindowNodeContainer 的作用

WindowNodeContainer 与 Display 一一对应,其管理了单个 Display 中的所有窗口,WindowRoot 则管理了所有的窗口与 WindowNodeContainer。WindowNodeContainer 提供了布局策略的决策与设置、窗口焦点设置、窗口排列、避开区域管理、窗口分屏显示等能力。

布局策略

OH 目前支持两种策略,CASCADE(层叠)与 TILE(平铺)。默认的布局策略是 CASCADE,分屏显示也会将策略切换至 CASCADE。布局策略的主要能力就是决定窗口的排列布局方式、位置与大小。两种策略的区别如下:

wKgaomXlvAWALf5cAAAWJdArVoE132.png

总结

wKgZomXlvAyAHfszAABY-5gLjRM805.png

设置全屏

设置全屏可以通过 ets 调用 window.setFullScreen(true),window 会占满全屏,并且状态栏与导航栏会消失。接下来来看看底层是如何实现的。

setFullScreen 会走到 WindowImpl 中,其中主要做了 3 件事

  • SetWindowMode(WindowMode::WINDOW_MODE_FULLSCREEN)
  • RemoveWindowFlag(WindowFlag::WINDOW_FLAG_NEED_AVOID)
  • 通过 SetSystemBarProperty 将状态栏与导航栏的 enable 置为 false

SetWindowMode
代码会调用到 WindowController::SetWindowMode 内,其中会对 mode 做一些判断。针对 FULLSCREEN 的情况,会最小化其他 app 的 window
接着调用 WindowNodeContainer::UpdateWindowNode,其中会调用布局策略来更新窗口的布局

RemoveWindowFlag
为窗口的 property 这是 flag 后,同样会走到 WindowNodeContainer::UpdateWindowNode 中
与窗口显示流程一样,其判断为全屏窗口后,不会避开状态栏与导航栏区域

SetSystemBarProperty
SetSystemBarProperty 同样会在 WindowNodeContainer 中更新窗口
迭代所有窗口,遇到全屏窗口,就将窗口内的 SystemBarProperty 与默认的对比,有变化(enable 值不同)就通知订阅了 systemBarTintChange 事件的组件
即 ets 中:window.on(‘systemBarTintChange’)
systemui 订阅了该事件,在收到事件后,根据 enable 的值,去调用 statusBar/navigationBar 窗口的 hide 方法,来达到隐藏状态栏导航栏的目的

如何设置全屏并且显示状态栏导航栏

只需要在调用 window 的 setFullScreen 函数后,在调用其 setSystemBarEnable 即可:

window.setSystemBarEnable(['status', 'navigation']).then(() => {})

加载 ui

在 Stage 模式中,我们通过 WindowStage 的 setUIContent 来加载页面,这个过程是如何实现的?WindowStage 是 WMS 提供给前端的一套 api,其通过调用 WindowImpl 的 setUIContent 来实现:

uiContent_ = Ace::UIContent::Create(context_.get(), engine)
uiContent_->Initialize(this, contentInfo, storage)

WindowImpl 会在合适的时机,调用 UIContent 内的回调:

  • uiContent_->UpdateViewportConfig(config, reason) 宽高位置等变化
  • uiContent_->UpdateWindowMode(mode)
  • uiContent_->ProcessBackPressed()
  • uiContent_->ProcessKeyEvent(keyEvent)
  • uiContent_->ProcessPointerEvent(pointerEvent)
  • uiContent_->ProcessVsyncEvent(static_cast(timeStamp))
  • uiContent_->UpdateConfiguration(configuration) 系统语言、颜色模式等变化

触摸事件的传递

触摸事件由多模输入模块传递到窗口,经过处理后,传递给 ACE 中的 UIContent 中。

通过 InputManager 注册为窗口输入事件消费者
触摸事件会回调至 WindowInputChannel::HandlePointerEvent 中
如果调用了窗口的 AddInputEventListener 设置触摸监听,转发事件至监听内,并且只将 POINTER_ACTION_DOWN 与 POINTER_ACTION_BUTTON_DOWN 传递给窗口。
如果是 POINTER_ACTION_MOVE 事件,在下一帧将事件传递给窗口。如果是其他事件,立即传递给窗口。
在窗口内,如果是悬浮窗口:

  • POINTER_ACTION_DOWN
    • 判断手指是否落在窗口之外,窗口 Decorate 矩形内,如果是,开启拖拽模式
    • 如果触摸的 window 类型为 WINDOW_TYPE_DOCK_SLICE,开始移动模式
  • POINTER_ACTION_MOVE
    • 如果开启拖拽模式,根据手指移动的距离,通过 WindowNodeContainer 修改窗口大小
    • 如果开启移动模式,根据手指移动的距离,通过 WindowNodeContainer 修改窗口位置
  • 如果开启了开启拖拽或移动模式,事件不会继续传递,如果未开启,则会传递给 ACE 的 UIContent

Display 管理流程分析

DMS 启动流程

DMS 在启动时的主要工作就是从 RenderService 获取屏幕信息,并创建 ScreenGroup 与 Display

通过 RSInterface 注册屏幕连接回调,在屏幕连接后,创建 AbsScreen

再通过 RSInterface 获取屏幕支持的分辨率、刷新率等信息,设置到 AbsScreen 中

创建 ScreenGroup,将 AbsScreen 添加到 group 中

添加后会为 AbstractScreen 初始化 RSDisplayNode,并向 RenderService 提交一条 RSDisplayNode 创建的事务

ScreenGroup 与 AbsScreen 初始化完毕后,会为 * *

AbsScreen 创建一个 AbstractDisplay
AbstractDisplay 保存了 AbsScreen 中的分辨率刷新率等信息,并且根据屏幕宽与高,决定虚拟像素比。

通知感兴趣的部件,Display 已经创建好

WMS 也会通过 DMS 监听 Display 的变化,比如大小变化、横竖屏变化,WMS 会通知到 WindowNodeContainer 去更新 Display 的状态并重新布局所有窗口

如果遇到 BEFORE_SUSPEND 事件,比如进入锁屏状态,WMS 会通过 WindowNodeContainer 通知每个窗口(WindowImpl)更新其状态为 STATE_FROZEN,并告知 AMS,让持有窗口的 Ability 进入后台状态

ScreenGroup 是什么

ScreenGroup 顾名思义是屏幕组,屏幕组中定义了多个屏幕的连接方式,如扩展或镜像。每个物理屏幕在连接后都会加入到默认的屏幕组中。屏幕组也可以包含虚拟屏幕。ScreenGroup 与 AbsScreen 都由 AbstractScreenController 管理。

UpdateRSTree

UpdateRSTree 会在窗口节点显示或隐藏时调用,其作用就是为 AbsScreen 中的 RSDisplayNode 添加或删除窗口的 RSSurfaceNode,并向向 RenderService 提交增加或删除子节点的事务。

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 子系统
    +关注

    关注

    0

    文章

    109

    浏览量

    12370
  • DMS
    DMS
    +关注

    关注

    0

    文章

    79

    浏览量

    16185
  • WMS
    WMS
    +关注

    关注

    1

    文章

    107

    浏览量

    5620
  • 鸿蒙
    +关注

    关注

    57

    文章

    2301

    浏览量

    42666
收藏 人收藏

    评论

    相关推荐

    Linux应用编程的基本概念

    Linux应用编程涉及到在Linux环境下开发和运行应用程序的一系列概念。以下是一些涵盖Linux应用编程的基本概念
    的头像 发表于 10-24 17:19 152次阅读

    时序逻辑电路的基本概念、组成、分类及设计方法

    时序逻辑电路是数字电路中的一种重要类型,它不仅在计算机、通信、控制等领域有着广泛的应用,而且对于理解和设计现代电子系统具有重要意义。 1. 时序逻辑电路的基本概念 时序逻辑电路(Sequential
    的头像 发表于 08-28 11:45 532次阅读

    伺服系统基本概念和与变频的关系

    伺服系统基本概念是准确、精确、快速定位。这一概念贯穿于伺服系统的设计理念和运行机制中。为了实现这一目标,伺服系统采用了多种先进的控制策略和
    的头像 发表于 08-27 15:59 297次阅读

    socket的基本概念和原理

    的通信。它是一个抽象的概念,用于表示网络中的一个通信实体。在计算机网络中,Socket允许应用程序通过网络发送和接收数据。Socket的概念最早由UNIX操作系统引入,后来被广泛应用于各种操作
    的头像 发表于 08-16 10:51 750次阅读

    卷积神经网络的基本概念、原理及特点

    基本概念、原理、特点以及在不同领域的应用情况。 一、卷积神经网络的基本概念 卷积神经网络是一种深度学习算法,它由多层卷积层和池化层堆叠而成。卷积层负责提取图像中的局部特征,而池化层则负责降低特征的空间维度,同时增加对图像位移的不变性。通过这种方式,CNN能够自
    的头像 发表于 07-11 14:38 685次阅读

    循环神经网络的基本概念

    循环神经网络的基本概念、循环机制、长短时记忆网络(LSTM)、门控循环单元(GRU)等方面进行介绍。 循环神经网络的基本概念 循环神经网络是一种时间序列模型,其基本思想是将序列数据中的每个元素(例如,单词、时间点等)作为输入,通过循环结构将前一个时间步的
    的头像 发表于 07-04 14:31 507次阅读

    串口通信的基本概念

    串口通信(Serial Communications)的基本概念可以归纳为以下几个方面:
    的头像 发表于 06-12 09:28 495次阅读
    串口通信的<b class='flag-5'>基本概念</b>

    RTC实时时钟的基本概念和工作原理

    精确的实时时间,并为电子系统提供精确的时间基准。本文将详细阐述RTC实时时钟的基本概念、工作原理以及其在现代电子设备中的应用。
    的头像 发表于 05-27 15:43 3187次阅读

    接地装置的基本概念

    接地装置的基本概念
    的头像 发表于 12-05 15:49 517次阅读
    接地装置的<b class='flag-5'>基本概念</b>

    工程师必看!电路基本概念有哪些?

    工程师必看!电路基本概念有哪些?
    的头像 发表于 11-30 09:31 623次阅读
    工程师必看!电路<b class='flag-5'>基本概念</b>有哪些?

    ros的基本概念是什么

    基本概念: ROS是一个用于在不同进程间匿名的发布、订阅、传递信息的中间件。 ROS2系统的核心部分是ROS网络(ROS Graph)。 ROS网络是指在ROS系统中不同的节点间相互通信的连接
    的头像 发表于 11-27 11:21 1711次阅读

    MMU相关的基本概念

    1-MMU相关的基本概念 (1)虚拟地址相关基本概念 • 虚拟内存(Virtual Memory,VM):为每个进程提供了一致的、连续的、私有的内存空间,简化了内存管理。将主存看成是一个存储在磁盘
    的头像 发表于 11-26 16:11 635次阅读

    线性稳压器和开关模式电源(SMPS)的基本概念

    电子发烧友网站提供《线性稳压器和开关模式电源(SMPS)的基本概念.pdf》资料免费下载
    发表于 11-24 14:47 0次下载
    线性稳压器和开关模式电源(SMPS)的<b class='flag-5'>基本概念</b>

    C语言的基本概念和编程技术

    电子发烧友网站提供《C语言的基本概念和编程技术.pdf》资料免费下载
    发表于 11-20 10:18 0次下载
    C语言的<b class='flag-5'>基本概念</b>和编程技术

    线程池基本概念与原理

    一、线程池基本概念与原理 1.1 线程池概念及优势 C++线程池简介 线程池是一种并发编程技术,它能有效地管理并发的线程、减少资源占用和提高程序的性能。C++线程池通过库,结合C++ 11、14
    的头像 发表于 11-10 10:24 455次阅读