尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

HarmonyOS 弦乐调音器开发实战 05:UIAbility 如何串起启动、窗口与配置更新

HarmonyOS 弦乐调音器开发实战 05:UIAbility 如何串起启动、窗口与配置更新 拿到一个 HarmonyOS Stage 模型应用时看到EntryAbility.ets并不等于已经理解了启动过程。应用对象何时建立共享状态主窗口何时出现页面何时加载窗口尺寸何时可读Preferences 服务又是在什么时候初始化这些动作如果混在一句“应用启动后进入首页”里很多时序风险会被遮住。弦乐调音器根目录的原生 ArkTS 工程给出了一个可以逐行核对的实例UIAbility 负责建立会话状态WindowStage 负责承载页面窗口对象负责报告尺寸和系统栏设置与历史服务则在内容加载成功后异步初始化。本文只分析根 ArkTS 工程不讨论 Flutter 子工程也不重复第 01 篇的双工程版本辨识。文中的六张图都是依据当前源码绘制的证据图不是当前版本运行截图。系列基线在 2026-08-03 记录 HDC 为[Empty]所以本文不会把编译通过或源码回调写成已经完成实机启动、前后台恢复和备份恢复验证。一、与前四篇的分界从“这是 Stage 工程”深入到“Stage 何时做什么”第 01 篇已经确认根工程的apiType是stageMode入口是EntryAbility → pages/Index第 02 篇沿 AudioCapturer 和音高检测链路展开第 03 篇关注参考音、历史和 AppStorage第 04 篇则切换到 Flutter 与 ArkTS 插件。本文不再复述版本号、双工程目录或算法参数而是回答一个更窄的问题原生进程从创建 Ability 到页面可见之间哪些代码在什么位置执行。项目相对路径entry/build-profile.json5{ apiType: stageMode, buildOption: { resOptions: { copyCodeResource: { enable: false } } },这段配置只确认模块按 Stage 模型构建并不能替代生命周期源码。真正的主入口还要继续核对module.json5中的mainElement、Ability 的srcEntry以及 UIAbility 类中的回调实现。把这三个层次分开能避免把“构建模型”“入口声明”和“运行时动作”混成同一个结论。二、模块声明只负责找到入口不负责执行页面业务entry 模块把EntryAbility指定为主元素并声明它能够响应系统桌面入口动作。系统可以据此找到 Ability 类但模块描述不会自动初始化主题、AppStorage、窗口监听或业务服务。那些行为必须在 Ability 源码里才能得到证明。项目相对路径entry/src/main/module.json5mainElement: EntryAbility, deviceTypes: [ phone, tablet, 2in1 ], deliveryWithInstall: true, installationFree: false, pages: $profile:main_pages, abilities: [ { name: EntryAbility, srcEntry: ./ets/entryability/EntryAbility.ets,这里还能看到 phone、tablet、2in1 三类设备声明但本文不会把它扩写成三类设备已经运行成功。设备声明与响应式页面如何实现会放在第 07 篇此处只取它作为 Ability 所属模块的静态配置事实。三、onCreate 先建立进程内默认状态EntryAbility继承UIAbility。onCreate()的第一组动作是尝试让应用颜色模式保持跟随系统然后用 AppStorage 建立默认键。这里的默认值具有“先让页面可用”的意义即使 SettingsService 尚未完成 Preferences 加载页面也能读取主题、A4、当前乐器、调音模式和窗口宽度等初值。项目相对路径entry/src/main/ets/entryability/EntryAbility.etsonCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { try { this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); } catch (err) { hilog.error(DOMAIN, testTag, Failed to set colorMode. Cause: %{public}s, JSON.stringify(err)); } AppStorage.setOrCreate(appContext, this.context); AppStorage.setOrCreate(currentTheme, dark); AppStorage.setOrCreate(themePreference, system); AppStorage.setOrCreate(systemIsDark, false);随后建立的 A4、显示单位、指针灵敏度、自动息屏、采样率、位深、输入源、当前乐器、调音模式、隐私与麦克风授权状态同样都是默认会话值。setOrCreate的语义也值得注意键存在时更新其值键不存在时创建它仍只是 AppStorage 会话状态不等于 Preferences 持久化。本文能确认的是默认键在 Ability 创建时被准备不能据此声称每个设置都已在底层业务生效第 03 篇已经逐项审计过消费者边界。四、系统主题的首轮解析也发生在 onCreate默认值建立后代码读取this.context.config.colorMode。若当前配置为深色就把systemIsDark设为 true并把currentTheme解析成 dark否则解析成 light。这个顺序意味着页面最初可能先拿到 hard-coded 默认值再在同一个onCreate回调中被系统配置结果校正。需要精确区分三个值themePreference表示用户选择的偏好systemIsDark表示当前系统配置currentTheme表示页面最终采用的运行主题。它们不是同一个字段。手动主题、限定资源目录和系统栏颜色之间的双轨关系会在第 09 篇展开本文只说明 Ability 是这条状态链的起点。五、onWindowStageCreate 才进入主窗口阶段Ability 创建完成不代表页面已经装入窗口。onWindowStageCreate()接到 WindowStage 后调用loadContent(pages/Index)并在回调中检查错误码。只有内容加载成功后续的主窗口读取、窗口尺寸监听以及服务初始化才会继续执行。项目相对路径entry/src/main/ets/entryability/EntryAbility.etsonWindowStageCreate(windowStage: window.WindowStage): void { // Main window is created, set main page for this ability hilog.info(DOMAIN, testTag, %{public}s, Ability onWindowStageCreate); windowStage.loadContent(pages/Index, (err) { if (err.code) { hilog.error(DOMAIN, testTag, Failed to load the content. Cause: %{public}s, JSON.stringify(err)); return; } hilog.info(DOMAIN, testTag, Succeeded in loading the content.);这段早返回很关键如果pages/Index加载失败当前回调不会继续初始化窗口监听和两项服务。因此“Ability 已创建”与“首页内容已加载”必须分开记录。文章或日志分析时也不能只看到Ability onCreate就判定首页已经成功显示。六、窗口宽度先从物理像素换算成 vp内容加载成功后代码异步取得主窗口读取默认显示设备的densityPixels再用窗口矩形的物理像素宽度除以密度四舍五入后写入 AppStorage 的windowWidth。初始值 360 只是兜底真正的宽度来自窗口属性。项目相对路径entry/src/main/ets/entryability/EntryAbility.etsconst disp display.getDefaultDisplaySync(); const dp disp.densityPixels 0 ? disp.densityPixels : 3; const rect mainWin.getWindowProperties().windowRect; AppStorage.setOrCreate(windowWidth, Math.round(rect.width / dp)); } catch (_e) {} mainWin.on(windowSizeChange, (size: window.Size) { try { const disp display.getDefaultDisplaySync(); const dp disp.densityPixels 0 ? disp.densityPixels : 3; AppStorage.setOrCreate(windowWidth, Math.round(size.width / dp)); } catch (_e) {} });同一逻辑还绑定到windowSizeChange所以自由窗口或设备形态变化时会重新计算宽度。当前源码没有保存监听句柄也没有在onWindowStageDestroy()中看到对应的off这是可以记录的资源释放缺口。另一个边界是代码使用默认显示设备密度在复杂多显示场景中是否始终与主窗口所在显示一致需要真实设备和官方接口语义进一步验证不能从源码直接作肯定结论。七、服务初始化被放在页面加载成功之后SettingsService.getInstance().init(this.context)和HistoryService.getInstance().init(this.context)都位于loadContent成功回调里。这样做让页面外壳能够先装入窗口再由服务异步读取 Preferences。代价是页面首轮渲染与服务完成加载之间可能存在时间差因此项目使用 AppStorage 默认值托底并让页面在生命周期或 refresh tick 中再次刷新数据。这两个init()都用 Promise catch 记录错误没有阻塞loadContent回调也没有把失败状态送到 UI。由此可确认“初始化被发起”不能写成“设置和历史一定在首帧之前恢复完毕”。想验证真实时序需要 HiLog 时间戳、首帧记录和服务加载完成日志本轮没有连接设备不能给出启动耗时数字。八、配置更新与系统栏属于不同层次当系统配置变化时onConfigurationUpdate()更新systemIsDark。只有themePreference system时它才继续修改currentTheme。系统栏内容颜色则由独立的applyStatusBar()根据当前运行主题设置。项目相对路径entry/src/main/ets/entryability/EntryAbility.etsonConfigurationUpdate(newConfig: Configuration): void { const sysDark newConfig.colorMode ConfigurationConstant.ColorMode.COLOR_MODE_DARK; AppStorage.setOrCreate(systemIsDark, sysDark); const themePref AppStorage.getstring(themePreference) ?? system; if (themePref system) { AppStorage.setOrCreate(currentTheme, sysDark ? dark : light); } } static applyStatusBar(win: window.Window, theme: string): void { const contentColor theme dark ? #FFFFFF : #000000; win.setWindowSystemBarProperties({ statusBarContentColor: contentColor, navigationBarContentColor: contentColor }).catch((_e: Error) {}); }这里没有设置状态栏背景色只设置内容颜色失败也被静默捕获。源码能证明调用路径存在不能证明每个设备、窗口模式和系统版本上都已达到正确对比度。九、前后台与销毁回调目前主要是观察点onForeground()、onBackground()、onDestroy()和onWindowStageDestroy()当前只输出 HiLog。真正的音频客户端停止、定时器清理、参考音释放大多由页面组件的aboutToDisappear()或 active 状态变化处理。这种职责划分不一定错误但它意味着不能看到 Ability 回调名称就宣称已经实现完整的全局资源回收。如果进程直接进入后台但页面没有按预期触发相应组件路径哪些资源仍然存活需要设备日志验证。本文只记录当前代码所有权Ability 负责入口、窗口、配置和服务启动各页面负责自身音频、定时器与播放生命周期。十、BackupExtension 入口不等于已经完成数据备份module 配置声明了EntryBackupAbilitybackup_config.json也写着allowToBackupRestore: true。但是继续查看扩展源码onBackup()和onRestore()只有日志与一个已完成 Promise没有枚举 Preferences、复制文件、校验版本或处理恢复冲突。项目相对路径entry/src/main/ets/entrybackupability/EntryBackupAbility.etsexport default class EntryBackupAbility extends BackupExtensionAbility { async onBackup() { hilog.info(DOMAIN, testTag, onBackup ok); await Promise.resolve(); } async onRestore(bundleVersion: BundleVersion) { hilog.info(DOMAIN, testTag, onRestore ok %{public}s, JSON.stringify(bundleVersion)); await Promise.resolve(); } }因此最准确的表述是“工程已声明备份扩展入口回调可被系统发现”而不是“调音历史和用户设置已完成系统备份”。要把它升级为真实能力还需要明确备份对象、数据格式、容量、版本迁移、隐私约束、恢复幂等性并用卸载/重装或系统迁移流程验证恢复结果。十一、用证据分层结束启动分析本文可以确认entry 使用 Stage 模型系统通过 module 配置找到 EntryAbilityonCreate()建立 AppStorage 默认状态并解析首轮系统主题onWindowStageCreate()加载pages/Index内容加载成功后读取主窗口、监听宽度并发起设置与历史服务初始化系统配置变化时只在“跟随系统”偏好下更新当前主题备份扩展虽已声明但没有真实数据处理实现。本文不能确认当前包在 phone、tablet、2in1 上的启动耗时窗口监听在所有形态下是否准确切前后台后音频是否无残留系统栏在所有主题下是否可读或备份恢复是否真的保存任何记录。系列基线记录 HDC 为[Empty]所以这些结论必须继续标为未验证。用这种方式拆解 UIAbilityStage 模型就不再是项目模板中的一个名词而成为一条可以逐节点审计的启动与窗口责任链。
返回列表