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.mzph.cn/news/686659.shtml

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

相关文章

在 Geoserver 中添加自定义的室内坐标系

要在 Geoserver 中添加自定义的室内坐标系&#xff0c;您需要在数据目录中的 user_projections 文件夹下创建或编辑一个 epsg.properties 文件&#xff0c;然后在文件末尾添加您的坐标系的定义&#xff0c;使用 WKT&#xff08;Well-Known Text&#xff09;格式。您还需要为您的…

WordPress站点成功升级后的介绍页地址是什么?

我们一般在WordPress站点后台 >> 仪表盘 >> 更新中成功升级WordPress的话&#xff0c;最后打开的就是升级之后的版本介绍页。比如boke112百科前两天升级到WordPress 6.4.2后显示的介绍页如下图所示&#xff1a; 该介绍除了介绍当前版本修复了多少个问题及修补了多少…

ABC341 A-G

Toyota Programming Contest 2024#2&#xff08;AtCoder Beginner Contest 341&#xff09; - AtCoder B读不懂题卡了&#xff0c;F读假题卡了&#xff0c;开题开慢了rank了 A - Print 341 题意&#xff1a; 打印一串交替出现的包含N个0&#xff0c;N1个1的01串 代码&…

2024免费人像摄影后期处理工具Portraiture4.1

Portraiture作为一款智能磨皮插件&#xff0c;确实为Photoshop和Lightroom用户带来了极大的便利。通过其先进的人工智能算法&#xff0c;它能够自动识别并处理照片中的人物皮肤、头发和眉毛等部位&#xff0c;实现一键式的磨皮美化效果&#xff0c;极大地简化了后期处理的过程。…

Switch开关(antd-design组件库)简单使用

1.Switch开关 开关选择器。 2.何时使用 需要表示开关状态/两种状态之间的切换时&#xff1b; 和 checkbox 的区别是&#xff0c;切换 switch 会直接触发状态改变&#xff0c;而 checkbox 一般用于状态标记&#xff0c;需要和提交操作配合。 组件代码来自&#xff1a; 开关 Swit…

【leetcode题解C++】51.N皇后 and 76.最小覆盖子串

51. N皇后 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方…

用Python和OpenCV搭建自己的一维码和QRCode扫描仪(步骤 + 源码)

导 读 本文主要介绍使用Python和OpenCV搭建自己的一维码和QRCode扫描仪&#xff08;步骤 源码&#xff09;。 项目简介 本文我们将创建一个程序来扫描图像中的二维码和条形码。对于这个程序&#xff0c;我们需要三个包&#xff0c;分别是OpenCV、NumPy和pyzbar。大多数 Pyth…

python中线程/线程池,进程/进程池的创建

创建子线程 # 创建子线程t1 threading.Thread(targetjob,args(1,))# 执行子线程t1.start()# 等待子线程执行print("waiting threading")t1.join()print("threading done")创建子进程 # 创建子进程p1 multiprocessing.Process(targetjob,args(1,),name&qu…

linux kernel 内存踩踏之KASAN_HW_TAGS(MTE)(三)

一、背景 linux kernel 内存踩踏之KASAN&#xff08;一&#xff09;_kasan版本跟hasan版本区别-CSDN博客 linux kernel 内存踩踏之KASAN_SW_TAGS&#xff08;二&#xff09;-CSDN博客 最后来介绍一下KASAN_HW_TAGS&#xff0c;ARM64上就是MTE&#xff0c;这个特性在ARMv8.5支…

贝叶斯网络、概率图模型、全局语义、因果链、朴素贝叶斯模型、枚举推理、变量消元

文章目录 频率学派 vs. 贝叶斯学派贝叶斯学派Probability(概率):独立性/条件独立性:Probability Theory(概率论):Graphical models (概率图模型)什么是图模型(Graphical Models)图是什么计算机科学中的图模型:为什么图模型有用?图模型:统一框架图模型在机器学习中的…

@ 代码随想录算法训练营第8周(C语言)|Day51(动态规划)

代码随想录算法训练营第8周&#xff08;C语言&#xff09;|Day51&#xff08;动态规划&#xff09; Day51、动态规划&#xff08;包含题目 139.单词拆分 &#xff09; 139.单词拆分 题目描述 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict&#xff0c;判定 s 是…

C++数据结构与算法——栈与队列

C第二阶段——数据结构和算法&#xff0c;之前学过一点点数据结构&#xff0c;当时是基于Python来学习的&#xff0c;现在基于C查漏补缺&#xff0c;尤其是树的部分。这一部分计划一个月&#xff0c;主要利用代码随想录来学习&#xff0c;刷题使用力扣网站&#xff0c;不定时更…

在Ubuntu-12.04环境下使用新的Rust开发工具

在老旧的系统环境下使用Rust开发工具 笔者的一台硬件资源紧张的旧电脑安装了Ubuntu-12.04&#xff0c;希望在其上运行比较新的Rust开发工具&#xff0c;但却发现其因glibc较老&#xff0c;不能正常运行cargo工具&#xff0c;结果如下&#xff1a; yejqUNIX:~$ uname -a Linux…

电商+支付双系统项目------支付系统的构思

本篇文章会讲讲支付的一些相关的名词概念以及怎么去设计支付系统&#xff0c;通过理解支付的这些名词概念和支付系统的架构&#xff0c;为接下来写支付系统的代码做好准备。 目录 支付------支付资质 支付------支付场景 微信 付款码支付 Native支付 支付宝 条码付 扫码…

问卷设计初探:题目类型概览与注意事项梳理

问卷法常被人们应用于社会调查中&#xff0c;它能反馈出最真实的社会信息。所以&#xff0c;很多企业为了最大程度地了解市场&#xff0c;也经常使用问卷调查法进行研究。不过&#xff0c;想要发挥出问卷法的最大用处&#xff0c;前提是要将问卷设计规范并且可量化。 想要设计…

在JavaScript中的防抖函数 - 通过在React中构建自动完成功能来解释

当你将一个新应用推向生产环境时&#xff0c;你希望确保它用户友好。网站的性能是用户体验的关键部分。每个用户都希望网站及其内容能够快速加载。每一秒都是宝贵的&#xff0c;可能导致用户再也不会访问你的网站。 在本指南中&#xff0c;我们将了解JavaScript中一个非常重要…

SQL 精讲-MySql 常用函数,MySQL语句精讲和举例

FORMAT(数值,保留位数) 四舍五入 SELECT *,FORMAT(score/3,2) from studentROUND(数值,保留位数) 四舍五入 SELECT ROUND(score/3,2) from studentCONCAT(字符串 1,字符串 2) 字符串拼接 SELECT CONCAT(customer_name, (,address,)) from mt_customerLEFT(字符串,长度) 截取…

内存处理函数 内存池

内存处理函数 内存申请函数(stdlib.h) malloc 原形&#xff1a;void *malloc(size_t size); 功能&#xff1a;用于动态分配指定大小的内存块&#xff0c;分配的内存块不会被初始化&#xff0c;内容是未定义的。 返回值&#xff1a;若分配成功&#xff0c;返回一个指向该内存块…

同步缓冲器设计(Synchronous FIFO)

缓冲器又称为队列&#xff0c;是一种数据暂存的机制。缓冲器存在的必要是因为当数据传出端传出数据时&#xff0c;接收端不一定能实时地接收&#xff0c;如果接收端不能实时地接收&#xff0c;又无缓冲器的存在&#xff0c;数据将不可避免会遗失。如果输出输入端的时钟频率一致…

2024.2.15 模拟实现 RabbitMQ —— 消息持久化

目录 引言 约定存储方式 消息序列化 重点理解 针对 MessageFileManager 单元测试 小结 统一硬盘操作​​​​​​​ 引言 问题&#xff1a; 关于 Message&#xff08;消息&#xff09;为啥在硬盘上存储&#xff1f; 回答&#xff1a; 消息操作并不涉及到复杂的增删查改消…