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

    文章

    100

    浏览量

    12264
  • DMS
    DMS
    +关注

    关注

    0

    文章

    75

    浏览量

    16073
  • WMS
    WMS
    +关注

    关注

    1

    文章

    94

    浏览量

    5576
  • 鸿蒙
    +关注

    关注

    55

    文章

    1629

    浏览量

    42119
收藏 人收藏

    评论

    相关推荐

    接地装置的基本概念

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

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

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

    天线的几个基本概念

    电子发烧友网站提供《天线的几个基本概念.ppt》资料免费下载
    发表于 10-07 16:17 0次下载
    天线的几个<b class='flag-5'>基本概念</b>

    电子电路的基本概念和理论分析

    本文旨在探讨电子电路学习的各个方面,包括基本概念、理论分析、实验设计与实现、结果分析以及应用与展望。通过对电子电路领域的深入研究,我们将揭示这一学科的魅力和挑战,并阐述其在现代科技领域中的重要地位。
    的头像 发表于 08-24 09:20 1391次阅读

    时序分析基本概念介绍—花一样的“模式”

    今天要介绍的时序基本概念是Mode(模式). 这是Multiple Scenario环境下Sign off的一个重要概念。芯片的设计模式包括最基本的功能function模式,以及各种各样相关的测试模式。
    的头像 发表于 07-10 17:21 1976次阅读
    时序<b class='flag-5'>分析</b><b class='flag-5'>基本概念</b>介绍—花一样的“模式”

    时序分析基本概念介绍&lt;ILM&gt;

    今天我们要介绍的时序分析基本概念是ILM, 全称Interface Logic Model。是一种block的结构模型。
    的头像 发表于 07-07 17:26 2232次阅读
    时序<b class='flag-5'>分析</b><b class='flag-5'>基本概念</b>介绍&lt;ILM&gt;

    时序分析基本概念介绍&lt;Virtual Clock&gt;

    今天我们介绍的时序分析基本概念是Virtual Clock,中文名称是虚拟时钟。
    的头像 发表于 07-07 16:52 788次阅读
    时序<b class='flag-5'>分析</b><b class='flag-5'>基本概念</b>介绍&lt;Virtual Clock&gt;

    时序分析基本概念介绍—Timing Arc

    今天我们要介绍的时序基本概念是Timing arc,中文名时序弧。这是timing计算最基本的组成元素,在昨天的lib库介绍中,大部分时序信息都以Timing arc呈现。
    的头像 发表于 07-06 15:00 1580次阅读
    时序<b class='flag-5'>分析</b><b class='flag-5'>基本概念</b>介绍—Timing Arc

    时序分析Slew/Transition基本概念介绍

    今天要介绍的时序分析基本概念是Slew,信号转换时间,也被称为transition time。
    的头像 发表于 07-05 14:50 1695次阅读
    时序<b class='flag-5'>分析</b>Slew/Transition<b class='flag-5'>基本概念</b>介绍

    时序分析基本概念介绍&lt;Skew&gt;

    今天要介绍的时序分析基本概念是skew,我们称为偏差。
    的头像 发表于 07-05 10:29 2364次阅读
    时序<b class='flag-5'>分析</b><b class='flag-5'>基本概念</b>介绍&lt;Skew&gt;

    介绍时序分析基本概念MMMC

    今天我们要介绍的时序分析基本概念是MMMC分析(MCMM)。全称是multi-mode, multi-corner, 多模式多端角分析模式。这是在先进工艺下必须要使用的一种时序
    的头像 发表于 07-04 15:40 1593次阅读
    介绍时序<b class='flag-5'>分析</b><b class='flag-5'>基本概念</b>MMMC

    时序分析基本概念介绍&lt;Latency&gt;

    今天要介绍的时序分析基本概念是Latency, 时钟传播延迟。主要指从Clock源到时序组件Clock输入端的延迟时间。
    的头像 发表于 07-04 15:37 1474次阅读
    时序<b class='flag-5'>分析</b><b class='flag-5'>基本概念</b>介绍&lt;Latency&gt;

    介绍时序分析基本概念lookup table

    今天要介绍的时序分析基本概念是lookup table。中文全称时序查找表。
    的头像 发表于 07-03 14:30 755次阅读
    介绍时序<b class='flag-5'>分析</b>的<b class='flag-5'>基本概念</b>lookup table

    静态时序分析基本概念和方法

    向量和动态仿真 。本文将介绍静态时序分析基本概念和方法,包括时序约束,时序路径,时序裕量,setup检查和hold检查等。 时序路径 同步电路设计中,时序是一个主要的考虑因素,它影响了电路的性能和功能。为了验证电路是否能在最
    的头像 发表于 06-28 09:38 802次阅读
    静态时序<b class='flag-5'>分析</b>的<b class='flag-5'>基本概念</b>和方法

    德索讲解LVDS线束的基本概念

    德索精密工业工程师指出,线束(circuit group)为一定负载源组提供服务设备的总体,如中继线路、交换装置、控制系统等。话务理论的基本研究内容是研究话务量、呼损和线束容量三者之间的关系,因此线束是话务理论中一个重要的基本概念
    的头像 发表于 06-12 09:25 2748次阅读
    德索讲解LVDS线束的<b class='flag-5'>基本概念</b>