OpenHarmony系统解决方案 - 配置屏幕方向导致开机动画和Launcher显示异常

问题环境

系统版本:OpenHarmony-3.2-Release

问题现象

  1. 配置设备默认方向,例如修改为横屏显示,修改文件display_manager_config.xmlbuildInDefaultOrientation参数值为2(Orientation::HORIZONTAL)。

源码中文件位于foundation/window/window_manager/resources/config/rk3568/display_manager_config.xml。

系统中文件位于/etc/window/resources/display_manager_config.xml。

  1. 系统启动后开机动画横竖屏切换,Launcher显示异常(偶现,去掉锁屏应用和锁屏服务后大概率出现)。
异常效果:

正常效果:

问题原因

  • ScreenRotationController初始化会设置rotationLockedRotation_属性初始值,而ScreenRotationController初始化的触发点在开机动画窗口销毁时,此时间点在LauncherWindow加载之后。
  • Launcher加载Window时会设置SetScreenRotation(屏幕旋转角度),因为Launcher的方向加载配置为AUTO_ROTATION_RESTRICTED(方向随传感器旋转),所以SetScreenRotation会根据rotationLockedRotation_属性值设置旋转角度,而此时rotationLockedRotation_属性并未被设置初始值,所以SetScreenRotation设置的值取得是默认值0(如果配置为Orientation::HORIZONTAL,则应旋转90度,取值为1),导致问题的产生。

解决方案

调整ScreenRotationController初始化时序,使ScreenRotationControllerLauncher加载Window时触发。修改源码文件:foundation/window/window_manager/wmserver/src/window_node_container.cpp

  1. WindowNodeContainer::RemoveWindowNode函数中,移除以下代码:
if (node->GetWindowType() == WindowType::WINDOW_TYPE_BOOT_ANIMATION) {DisplayManagerServiceInner::GetInstance().SetGravitySensorSubscriptionEnabled();
}

修改后WindowNodeContainer::RemoveWindowNode函数代码:

// foundation/window/window_manager/wmserver/src/window_node_container.cpp
WMError WindowNodeContainer::RemoveWindowNode(sptr<WindowNode>& node, bool fromAnimation)
{···NotifyIfAvoidAreaChanged(node, AvoidControlType::AVOID_NODE_REMOVE);DumpScreenWindowTree();UpdateCameraFloatWindowStatus(node, false);if (node->GetWindowType() == WindowType::WINDOW_TYPE_KEYGUARD) {isScreenLocked_ = false;SetBelowScreenlockVisible(node, true);}WLOGFD("RemoveWindowNode windowId: %{public}u end", node->GetWindowId());RSInterfaces::GetInstance().SetAppWindowNum(GetAppWindowNum());return WMError::WM_OK;
}
  1. WindowNodeContainer::AddWindowNode函数中,在WLOGFD(“AddWindowNode windowId: %{public}u end”, node->GetWindowId());行代码前添加以下代码:
if (node->GetWindowType() == WindowType::WINDOW_TYPE_DESKTOP) {DisplayManagerServiceInner::GetInstance().SetGravitySensorSubscriptionEnabled();
}

修改后WindowNodeContainer::AddWindowNode函数代码:

WMError WindowNodeContainer::AddWindowNode(sptr<WindowNode>& node, sptr<WindowNode>& parentNode, bool afterAnimation)
{···if (node->GetWindowType() == WindowType::WINDOW_TYPE_WALLPAPER) {RemoteAnimation::NotifyAnimationUpdateWallpaper(node);}if (node->GetWindowType() == WindowType::WINDOW_TYPE_DESKTOP) {DisplayManagerServiceInner::GetInstance().SetGravitySensorSubscriptionEnabled();}WLOGFD("AddWindowNode windowId: %{public}u end", node->GetWindowId());RSInterfaces::GetInstance().SetAppWindowNum(GetAppWindowNum());return WMError::WM_OK;
}

定位过程

  1. 落盘异常开机日志,查找SetRotation相关日志,发现系统启动过程中横竖屏被设置两次。
08-05 18:39:55.002   622   811 I C04201/AbstractScreenController: <722>SetRotation: Enter SetRotation, screenId: 0, rotation: 1, isFromWindow: 1
08-05 18:39:58.487   622   811 I C04201/AbstractScreenController: <722>SetRotation: Enter SetRotation, screenId: 0, rotation: 0, isFromWindow: 1
  1. 查找对应源码发现rotation代表含义。在系统启动时已成功设置旋转90度(水平),但又被设置为旋转0度(垂直),导致异常。
// foundation/window/window_manager/interfaces/innerkits/dm/dm_common.h
enum class Rotation : uint32_t {ROTATION_0,          // 不旋转,垂直ROTATION_90,        // 旋转90度,水平ROTATION_180, 	 ROTATION_270,
};
// foundation/window/window_manager/dmserver/src/abstract_screen_controller.cpp
bool AbstractScreenController::SetRotation(ScreenId screenId, Rotation rotationAfter, bool isFromWindow)
{WLOGFI("Enter SetRotation, screenId: %{public}" PRIu64 ", rotation: %{public}u, isFromWindow: %{public}u",screenId, rotationAfter, isFromWindow);···
}
  1. 追踪设置旋转0度(垂直)操作日志。发现set orientation时,orientation被设置为8,对应源码含义为AUTO_ROTATION_RESTRICTED
08-05 18:39:58.487   622   811 D C04201/AbstractScreenController: <627>set orientation. screen 0 orientation 8
08-05 18:39:58.487   622   811 D C04201/AbstractScreenController: <144>GetAbstractScreen: screenId: 0
08-05 18:39:58.487   622   811 D C04201/AbstractScreenController: <177>GetDefaultAbstractScreenId: GetDefaultAbstractScreenId, screen:0
08-05 18:39:58.487   622   811 D C04201/DisplayManagerService: <190>GetDefaultDisplayInfo: GetDefaultDisplayInfo 0
08-05 18:39:58.487   622   811 D C04201/AbstractScreenController: <177>GetDefaultAbstractScreenId: GetDefaultAbstractScreenId, screen:0
08-05 18:39:58.487   622   811 D C04201/DisplayManagerService: <190>GetDefaultDisplayInfo: GetDefaultDisplayInfo 0
08-05 18:39:58.487   622   811 I C04201/AbstractScreenController: <722>SetRotation: Enter SetRotation, screenId: 0, rotation: 0, isFromWindow: 1

// foundation/window/window_manager/dmserver/src/abstract_screen_controller.cpp
bool AbstractScreenController::SetOrientation(ScreenId screenId, Orientation newOrientation, bool isFromWindow)
{WLOGD("set orientation. screen %{public}" PRIu64" orientation %{public}u", screenId, newOrientation);···
}
// foundation/window/window_manager/interfaces/innerkits/dm/dm_common.h
enum class Orientation : uint32_t {BEGIN = 0,UNSPECIFIED = BEGIN,VERTICAL = 1,HORIZONTAL = 2,REVERSE_VERTICAL = 3,REVERSE_HORIZONTAL = 4,SENSOR = 5,SENSOR_VERTICAL = 6,SENSOR_HORIZONTAL = 7,AUTO_ROTATION_RESTRICTED = 8,AUTO_ROTATION_PORTRAIT_RESTRICTED = 9,AUTO_ROTATION_LANDSCAPE_RESTRICTED = 10,LOCKED = 11,END = LOCKED,
};
  1. Launcher在创建window时会把PreferredOrientation设置为Window.Orientation.AUTO_ROTATION_RESTRICTED
// common/src/main/ets/default/manager/WindowManager.ts
createWindow(context: ServiceExtensionContext, name: string, windowType: number, loadContent: string,isShow: boolean, callback?: Function) {Window.create(context, name, windowType).then((win) => {void win.setPreferredOrientation(Window.Orientation.AUTO_ROTATION_RESTRICTED);···}, (error) => {Log.showError(TAG, `createWindow, create error: ${JSON.stringify(error)}`);});
}
  1. Launcher显示窗口时执行SetOrientation,isFromWindow参数为true
// foundation/window/window_manager/dmserver/src/abstract_screen_controller.cpp
bool AbstractScreenController::SetOrientation(ScreenId screenId, Orientation newOrientation, bool isFromWindow)
{WLOGD("set orientation. screen %{public}" PRIu64" orientation %{public}u", screenId, newOrientation);auto screen = GetAbstractScreen(screenId);···if (isFromWindow) {ScreenRotationController::ProcessOrientationSwitch(newOrientation); // 执行方向选择} else {Rotation rotationAfter = screen->CalcRotation(newOrientation);SetRotation(screenId, rotationAfter, false);screen->rotation_ = rotationAfter;}if (!screen->SetOrientation(newOrientation)) {WLOGE("fail to set rotation, screen %{public}" PRIu64"", screenId);return false;}···return true;
}
  1. orientationAUTO_ROTATION_RESTRICTED,会执行ProcessSwitchToSensorRelatedOrientation函数。
// foundation/window/window_manager/dmserver/src/screen_rotation_controller.cpp
void ScreenRotationController::ProcessOrientationSwitch(Orientation orientation)
{if (!IsSensorRelatedOrientation(orientation)) {ProcessSwitchToSensorUnrelatedOrientation(orientation);} else {ProcessSwitchToSensorRelatedOrientation(orientation, lastSensorRotationConverted_);}
}
bool ScreenRotationController::IsSensorRelatedOrientation(Orientation orientation)
{if ((orientation >= Orientation::UNSPECIFIED && orientation <= Orientation::REVERSE_HORIZONTAL) ||orientation == Orientation::LOCKED) {return false;}// AUTO_ROTATION_RESTRICTED 返回 truereturn true;
}
  1. rotationLockedRotation_GetCurrentDisplayRotation()不一致时会切换旋转角度。在此处增加日志打印rotationLockedRotation_GetCurrentDisplayRotation()的值,发现在开机触发Launcher设置屏幕旋转角度时GetCurrentDisplayRotation()函数获取的当前屏幕旋转角度为1(水平)是正确的。而rotationLockedRotation_0(垂直)。
// foundation/window/window_manager/dmserver/src/screen_rotation_controller.cpp
void ScreenRotationController::ProcessSwitchToSensorRelatedOrientation(Orientation orientation, DeviceRotation sensorRotationConverted){lastOrientationType_ = orientation;switch (orientation) {case Orientation::AUTO_ROTATION_RESTRICTED: {if (isScreenRotationLocked_) {SetScreenRotation(rotationLockedRotation_);return;}[[fallthrough]];}···}
}
void ScreenRotationController::SetScreenRotation(Rotation targetRotation){if (targetRotation == GetCurrentDisplayRotation()) {return;}DisplayManagerServiceInner::GetInstance().GetDefaultDisplay()->SetRotation(targetRotation);DisplayManagerServiceInner::GetInstance().SetRotationFromWindow(defaultDisplayId_, targetRotation);WLOGFI("dms: Set screen rotation: %{public}u", targetRotation);
}
  1. 查看rotationLockedRotation_被设置的场景。分别增加日志,发现开机启动时SetScreenRotationLocked函数不会被触发,而Init函数则是在Launcher启动后被触发,此时Launcher已经把屏幕旋转角度设置为0(垂直),rotationLockedRotation_的初始化值则会变成Launcher设置后的参数0(垂直)。而在Launcher触发SetScreenRotation时,rotationLockedRotation_还未被设置,此时取默认值0(垂直),导致异常的产生。
// foundation/window/window_manager/dmserver/src/screen_rotation_controller.cpp
void ScreenRotationController::Init()
{ProcessRotationMapping();currentDisplayRotation_ = GetCurrentDisplayRotation();lastSensorDecidedRotation_ = currentDisplayRotation_;rotationLockedRotation_ = currentDisplayRotation_;
}
void ScreenRotationController::SetScreenRotationLocked(bool isLocked)
{if (isLocked) {rotationLockedRotation_ = GetCurrentDisplayRotation();}isScreenRotationLocked_ = isLocked;
}
  1. ScreenRotationController::Init()的触发时机是在系统检测到启动完成后,关闭开机动画窗口时触发。如果此操作在Launcher加载Window之后,则会导致问题。改变ScreenRotationController::Init()的初始化时序,在Launcherwindow加载时初始化可以修复此问题。
// foundation/window/window_manager/wmserver/src/window_node_container.cpp
WMError WindowNodeContainer::RemoveWindowNode(sptr<WindowNode>& node, bool fromAnimation)
{···if (node->GetWindowType() == WindowType::WINDOW_TYPE_BOOT_ANIMATION) {DisplayManagerServiceInner::GetInstance().SetGravitySensorSubscriptionEnabled();}···return WMError::WM_OK;
}// foundation/window/window_manager/dmserver/src/display_manager_service.cpp
void DisplayManagerService::SetGravitySensorSubscriptionEnabled()
{···SensorConnector::SubscribeRotationSensor();
}// foundation/window/window_manager/dmserver/src/sensor_connector.cpp
void SensorConnector::SubscribeRotationSensor()
{WLOGFI("dms: subscribe rotation-related sensor");ScreenRotationController::Init();···
}

知识分享

如果应用的方向需要随系统切换,可以在module.json5ability中配置orientationauto_rotation_restricted

为了能让大家更好的学习鸿蒙 (OpenHarmony) 开发技术,这边特意整理了《鸿蒙 (OpenHarmony)开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙 (OpenHarmony)开发学习手册》

入门必看:https://qr21.cn/FV7h05

  1. 应用开发导读(ArkTS)
  2. ……

HarmonyOS 概念:https://qr21.cn/FV7h05

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全

如何快速入门?:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. 构建第一个JS应用
  4. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

1.Ability开发
2.UI开发
3.公共事件与通知
4.窗口管理
5.媒体
6.安全
7.网络与链接
8.电话服务
9.数据管理
10.后台任务(Background Task)管理
11.设备管理
12.设备使用信息统计
13.DFX
14.国际化开发
15.折叠屏系列
16.……

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

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

相关文章

适用于电脑和手机的照片恢复工具指南

这是适用于 Android、iPhone、Mac 和 Windows 的最佳照片恢复应用程序的指南。 如果您不小心删除了一堆珍贵的照片&#xff0c;请不要担心&#xff01; 恢复丢失的照片和数据实际上比您想象的要容易得多。 通过使用照片恢复应用程序&#xff0c;您可以“解锁”存储卡或硬盘驱…

php基础学习之文件包含

描述 在一个php脚本中&#xff0c;将另一个php文件包含进来&#xff0c;合作实现某种功能 这个描述看起来似乎和C/Java等语言的头文件/包有点类似&#xff0c;但本质是不一样的 打个比方&#xff1a; C/Java的头文件/包更像是一个工具箱&#xff0c;存放各种很完善的工具&#…

LabVIEW开发DUP实时监控系统

LabVIEW开发DUP实时监控系统 该项目采用虚拟仪器设计理念&#xff0c;以LabVIEW作为核心技术平台&#xff0c;开发了一套磁控溅射过程的实时监控系统。实现过程中关键参数的全面数据采集与处理&#xff0c;建立完整的历史数据库&#xff0c;以支持涂层技术的改进和系统向模糊控…

Leetcode1423.可获得的最大点数

文章目录 题目原题链接思路&#xff08;逆向思维&#xff09; 题目 原题链接 Leetcode1423.可获得的最大点数 思路&#xff08;逆向思维&#xff09; 由题目可知&#xff0c;从两侧选k张&#xff0c;总数为n张&#xff0c;即从中间选n - k张 nums总和固定&#xff0c;要选k张最…

RK3399平台开发系列讲解(调试篇)死锁检测工具lockdep

🚀返回专栏总目录 文章目录 一、常见死锁场景二、lockdep使用方法三、lockdep技术原理3.1、锁类状态3.2、检查规则沉淀、分享、成长,让自己和他人都能有所收获!😄 📢介绍死锁检测工具lockdep。 资料 一、常见死锁场景 场景1:进程重复申请同一个锁,称为AA死锁。例如…

深入理解lambda表达式

深入理解ASP.NET Core中的中间件和Lambda表达式 var builder WebApplication.CreateBuilder(args); var app builder.Build(); app.Use(async (context, next) > { // Add code before request. await next(context);// Add code after request.}); 这段C#代码是用于设…

【打工日常】使用docker部署Dashdot工具箱

一、Dashdot介绍 dashdot是一个简洁清晰的服务器数据仪表板&#xff0c;基于React实现 &#xff0c;主要是显示操作系统、进程、存储、内存、网络这五个的数据。 二、本次实践介绍 1. 本次实践简介 本次实践部署环境为个人测试环境 2. 本地环境规划 本次实践环境规划&#xf…

不错的PMO 2024建设规划长图

公众号"PMO前沿"是国内最大的PMO组织&#xff0c;经常各个城市举办线下线上活动&#xff0c;很多专家&#xff0c;相当赞&#xff0c;而且每天还分享不少文章&#xff08;春节都不停更&#xff0c;相当感动&#xff09;&#xff0c;建议关注。看到一个不错的PMO 组织…

社区居家养老新选择,全视通智慧方案让长者生活更安心

随着人口老龄化趋势加剧&#xff0c;养老问题已经成为社会各界关注的焦点。我国政府积极采取相关措施&#xff0c;加速推动养老服务业的健康发展。2023年5月&#xff0c;《城市居家适老化改造指导手册》发布&#xff0c;针对城市老年人居家适老化改造需求&#xff0c;提出了47项…

【Linux】程序地址空间 -- 详解 Linux 2.6 内核进程调度队列 -- 了解

一、程序地址空间回顾 在学习 C/C 时&#xff0c;我们知道内存会被分为几个区域&#xff1a;栈区、堆区、全局/静态区、代码区、字符常量区等。但这仅仅是在语言层面上的理解&#xff0c;是远远不够的。 如下空间布局图&#xff0c;请问这是物理内存吗&#xff1f; 不是&…

备战蓝桥杯---图论之最小生成树

首先&#xff0c;什么是最小生成树&#xff1f; 他就是无向图G中的所有生成树中树枝权值总和最小的。 如何求&#xff1f; 我们不妨采用以下的贪心策略&#xff1a; Prim算法&#xff08;复杂度&#xff1a;&#xff08;nm)logm)&#xff1a; 我们对于把上述的点看成两个集…

html从零开始10:注释与常见输出方式,数据类型,typeof运算符,运算符之算术、赋值、比较、布尔运算符【搬代码】

注释与常见输出方式 输出方式 数据类型 typeof运算符 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport…