04 Stage 模型、系统能力与数据架构

一、详细知识点

1. UIAbility

UIAbility 是带 UI 的应用组件,负责生命周期入口。常见回调包括创建、窗口创建、前台、后台和销毁。

ts 复制代码
export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/Index')
  }
}

2. WindowStage

WindowStage 负责窗口管理和页面加载。首页路径错、资源错或页面构建异常,都可能表现为启动失败或白屏。

3. Context

Context 是访问资源、文件、系统能力和运行环境的重要入口。工程化开发要明确 Context 生命周期,不要把短生命周期对象长期持有。

4. 页面导航

简单 demo 可用页面状态切换;生产项目应使用官方推荐导航能力组织页面栈、参数和返回行为。无论哪种方式,都要处理参数缺失和数据不存在。

5. 权限与系统能力

调用网络、位置、媒体、通知等能力时,要先声明权限,再按场景申请或检查。用户拒绝权限是正常路径,必须给替代方案。

6. 数据架构

Page 页面
Component 组件
Service 业务服务
Remote API / Mock API
Local Store / Preferences
Model 类型模型

工程要求:

  • Page 只做展示和交互。
  • Service 负责业务编排。
  • Store 负责本地状态和持久化。
  • Remote API 负责网络请求。
  • Model 做边界转换,避免脏数据进入 UI。

二、本章 demo

Demo 1:生命周期日志

EntryAbility.ets 中记录生命周期:

ts 复制代码
onForeground(): void {
  hilog.info(0x0000, 'HarmonyNews', 'onForeground')
}

onBackground(): void {
  hilog.info(0x0000, 'HarmonyNews', 'onBackground')
}

Demo 2:收藏状态封装

FavoriteStore.ets

ts 复制代码
static toggle(articleId: string): void {
  if (FavoriteStore.favoriteIds.has(articleId)) {
    FavoriteStore.favoriteIds.delete(articleId)
  } else {
    FavoriteStore.favoriteIds.add(articleId)
  }
}

Demo 3:页面只调用服务

Index.ets 不直接维护收藏集合,而是调用 FavoriteStore.toggle()NewsService.markFavorites(),保持页面薄。

三、面试题与详细答案

1. UIAbility 和页面是什么关系?

UIAbility 是应用组件入口,负责生命周期和窗口创建;页面是 ArkUI 渲染内容。UIAbility 通过 WindowStage.loadContent() 加载页面。页面不应该承担应用生命周期管理职责。

2. 为什么不能在生命周期里做重任务?

生命周期回调影响启动和前后台切换,如果做长时间同步任务会造成首屏慢、卡顿甚至 ANR 风险。重任务应拆到异步流程,首屏只做必要初始化。

3. 权限拒绝时应该怎么处理?

权限拒绝是用户选择,不是程序异常。应用应解释原因、提供降级能力、允许稍后再开启,而不是直接崩溃或强制退出。

4. Repository / Service 分层解决什么问题?

它把页面和数据源解耦。页面不关心数据来自网络、缓存还是 mock;测试时可以替换 Service;接口变更时集中修改转换逻辑。这是从 demo 走向生产工程的关键。

四、五倍扩展知识点矩阵

1. Stage 模型对象关系

对象 职责 常见使用 工程风险
UIAbility UI 应用组件入口 生命周期、窗口创建 生命周期阻塞
WindowStage 窗口舞台 加载页面 页面路径和窗口状态错误
Context 上下文 资源、文件、系统能力 错误持有导致泄漏
Want 启动参数 跨 Ability 传参 参数缺失和类型不匹配
AbilityStage HAP 级初始化 模块启动逻辑 初始化过重
ExtensionAbility 扩展能力 卡片、服务等 生命周期和权限差异
module.json5 模块声明 Ability、权限 配置与代码不一致
app.json5 应用身份 包名、版本 发布后变更风险
resources 资源 字符串、颜色、媒体 多语言和主题治理
hilog 日志 生命周期和错误 敏感信息泄露

2. 生命周期实践

回调 适合做 不适合做
onCreate 轻量初始化、日志 网络大请求、重 IO
onWindowStageCreate 加载首页、窗口配置 业务批处理
onForeground 恢复监听、刷新短期状态 重复创建资源
onBackground 保存草稿、暂停任务 开启新长任务
onDestroy 清理资源 发起不可控异步

生产实践理解:生命周期不是业务万能入口。它是应用运行状态变化的边界,业务初始化要分级:首屏必要、首屏后、用户触发、后台可延迟。

3. 权限治理

权限场景 产品问题 技术问题 合规问题
网络 为什么需要联网 失败和超时 数据传输说明
位置 是否必须精准定位 授权拒绝降级 高敏感信息
相机 是否必须拍摄 设备无能力 用户授权说明
相册 是否可替代 文件格式 最小访问范围
通知 是否打扰用户 通知开关 用户可关闭
麦克风 是否必要 录音失败 明确用途
文件 保存在哪里 空间不足 数据删除
后台任务 是否真需要 耗电 用户感知

4. 数据架构扩展

Page / Component
View State
Service / UseCase
Repository
Remote API
Local Store
DTO Mapper
Error Mapping

分层解释:

  • Page 只关心展示状态。
  • View State 组合 loading、error、data。
  • Service 表达业务动作,如刷新、收藏、保存设置。
  • Repository 屏蔽远程和本地数据源。
  • Mapper 处理 DTO 到领域模型。
  • Error Mapping 把底层错误变成用户可理解状态。

5. 缓存策略

策略 适合场景 风险
只读远程 实时性强 弱网体验差
先缓存后网络 新闻、配置 数据可能短暂旧
只本地 设置、草稿 多端同步差
写穿缓存 写后马上读 写失败处理复杂
定时刷新 首页推荐 耗电和流量
用户触发刷新 列表页 用户等待
分页缓存 长列表 缓存清理
版本化缓存 数据结构升级 迁移成本

五、系统能力 demo 扩展任务

任务 目标 验证
增加网络权限说明 学会权限声明 module.json5 有权限
模拟 HTTP 失败 学会错误状态 页面显示重试
增加缓存读取 学会本地优先 首屏先显示旧数据
增加设置持久化 学会 Preferences 思路 重启后保留设置
增加权限拒绝文案 学会降级 用户知道原因
增加详情参数校验 防止空参数 缺 id 显示空状态
增加刷新节流 防重复请求 连点不重复发起
增加 Repository 解耦页面和数据源 Page 不知道 mock
增加错误码映射 技术错误转业务错误 页面文案清晰
增加日志 traceId 排查链路 日志能串起来

扩展示例:Repository 接口

ts 复制代码
export interface NewsRepository {
  queryArticles(): Promise<NewsArticle[]>
  queryArticleById(id: string): Promise<NewsArticle | undefined>
  toggleFavorite(id: string): Promise<void>
}

接口化后,页面可以依赖抽象,测试时使用 FakeRepository,生产时使用 RemoteRepository + LocalStore。

六、数据流检查

检查项 好的表现 坏的表现
数据来源 页面知道状态,不知道接口细节 页面直接拼接口
错误处理 分技术错误和用户文案 catch 后静默
缓存 有版本和失效策略 永久保存不清理
权限 拒绝可降级 拒绝就崩溃
日志 有模块和动作 只有 error
安全 不记录敏感信息 token 写日志
测试 Service 可替换 只能真机点
并发 处理重复请求 状态互相覆盖
恢复 前后台状态合理 返回页面错乱
扩展 新数据源容易接 页面大改

七、扩展面试题

5. Stage 模型里为什么要区分 Ability 生命周期和页面生命周期?

Ability 生命周期描述应用组件的运行状态,页面生命周期描述 UI 组件的出现和消失。二者粒度不同。把页面数据加载都塞进 Ability 会让入口过重;把应用级资源管理放进页面又会导致重复初始化。

6. 权限最小化如何落地?

先从业务需求反推权限,能不用就不用,能低敏就不用高敏。配置文件只声明真实需要的权限,运行时在用户触发相关功能时申请,并提供拒绝后的降级路径。上线前检查权限说明和实际使用是否一致。

7. Repository 和 Store 的区别是什么?

Repository 负责统一数据访问,可以组合远程、本地和转换逻辑;Store 更偏本地状态或持久化状态管理。Repository 可以使用 Store,但页面最好通过 Service 或 Repository 获取业务数据。

8. 如何设计弱网体验?

首屏尽量展示缓存;请求要有超时;失败要有重试;关键操作要有明确状态;不要无限转圈。对于写操作,要说明是否成功、是否可重试、是否会重复提交。

9. Context 为什么不能随意长期持有?

Context 和运行环境、Ability 生命周期有关。随意长期持有可能造成生命周期错乱或资源无法释放。需要系统能力时应在合适作用域获取,并避免把短生命周期对象保存到全局单例中。

10. 如何从 demo 架构演进到生产架构?

先把 mock 数据移入 Service,再抽 Repository,再增加 LocalStore 和 RemoteAPI,再统一错误处理和日志,最后补测试和发布检查。每一步都保持页面调用方式稳定,避免 UI 随数据源变化大改。

八、Stage 与数据架构详解库

1. 生命周期是资源管理边界

前后台切换、窗口创建和销毁决定了资源何时创建、暂停、恢复和释放。网络轮询、动画、定位、监听器等都要和生命周期绑定,否则会带来耗电、内存和状态问题。

2. 首屏初始化要分级

首屏必要任务包括加载首页、读取最低限度设置、准备关键 UI。非必要任务如预加载推荐、同步历史、上传日志应延后。启动阶段越重,用户感知越差。

3. Want 参数必须校验

跨 Ability 或页面传参时,参数可能缺失、类型不对或来自旧版本。详情页根据 id 加载数据时,必须处理 id 为空和文章不存在。

4. 权限申请要绑定用户动作

应用启动就申请大量权限体验很差。更好的方式是在用户触发相关功能时说明原因并申请。拒绝后提供降级路径。

5. Repository 是数据源防火墙

页面不应该知道数据来自 mock、HTTP、缓存还是数据库。Repository 隐藏这些细节,让页面只处理业务状态。

6. 本地缓存要有失效策略

缓存不是永久保存。新闻列表可以设置时间失效,用户设置可以长期保存,登录态和敏感信息要按安全策略管理。

7. 弱网不是异常边角

移动应用大量场景都可能弱网。架构要内置超时、重试、缓存、错误提示和重复提交保护。

8. 日志要串联链路

一次用户操作可能经过 Page、Service、Repository、Remote 和 Store。日志如果没有 traceId 或上下文,很难定位问题发生在哪一层。

9. 多设备能力要先抽象

不同设备能力不同,页面不应直接假设所有系统能力都可用。要有能力检测、降级和设备差异策略。

10. 架构演进要小步保持可运行

不要一次性把 demo 重构成复杂架构。每次只引入一个边界:先 Service,再 Store,再 Repository,再 Remote。每一步都保持项目可运行。

九、Stage 与系统能力场景库

场景 架构动作 风险 验收
启动首页 Ability 加载页面 白屏 首页显示
前台恢复 刷新短状态 重复请求 日志清楚
后台切换 保存草稿 数据丢失 返回恢复
打开详情 校验 id 空参数 空状态
请求新闻 Service 调 Repository 网络失败 重试
收藏文章 Store 更新 状态不同步 列表和收藏页一致
设置主题 Store 持久化 重启丢失 重新打开保留
权限拒绝 降级提示 崩溃 用户可返回
缓存过期 重新拉取 旧数据 有刷新策略
弱网超时 错误映射 无限加载 出现提示
多设备 能力检测 调用失败 降级可用
日志追踪 traceId 无法定位 链路可串

十、扩展面试题库

11. 为什么首屏不应该等待所有接口?

用户首先需要看到可交互界面。等待所有接口会放大最慢接口的影响。应优先加载首屏必要数据,非关键数据延后或并发加载。

12. 如何设计缓存失效?

根据数据性质决定。新闻列表可以短时间缓存并允许手动刷新;用户设置长期有效;敏感数据要有安全和清理策略。缓存要记录版本和时间,避免旧结构解析失败。

13. 弱网下如何避免重复提交?

按钮点击后进入提交中状态,禁用重复点击;请求带业务幂等标识;失败时明确是否可重试;重试不能造成重复业务结果。

14. 为什么权限弹窗要延后到具体场景?

用户在具体场景下更容易理解权限用途。启动即申请会让用户困惑,也可能导致拒绝率上升。延后申请还能减少不必要权限。

15. 如何判断架构是否过度设计?

如果抽象没有减少重复、没有隔离变化、没有提升测试性,只是增加文件和跳转,就是过度设计。架构应服务当前复杂度,并允许未来平滑扩展。

十一、系统能力知识体系补全

能力域 关键能力 封装建议 验收点
应用框架 UIAbility、ExtensionAbility、Want、Context AbilityService 生命周期日志完整
导航与启动 router、Navigation、深链、Want 参数 NavigationService 参数缺失可恢复
权限 声明、申请、拒绝、撤回 PermissionService 拒绝不崩溃
网络 HTTP、超时、重试、弱网 HttpClient 失败有错误映射
数据存储 Preferences、文件、relationalStore LocalDataSource 缓存可读写
媒体 图片、相册、音视频、拍摄 MediaService 授权和失败处理
通知 通知权限、频道、点击跳转 NotificationService 可开启可关闭
后台任务 短任务、延迟任务、WorkScheduler 类场景 BackgroundTaskService 不滥用后台
并发 taskpool、worker、异步队列 TaskService UI 不阻塞
设备能力 窗口、屏幕、设备类型、传感器 DeviceService 多设备可降级
卡片 FormExtensionAbility、卡片数据刷新 WidgetService 卡片独立可用
分布式/协同 多设备流转、数据协同场景 CollaborationService 状态迁移清楚
日志 hilog、traceId、分级 Logger 日志可串联

十二、数据架构分层方案

Page / Component
ViewState
UseCase / Service
Repository Interface
RemoteDataSource
LocalDataSource
MemoryCache
DTO
Mapper
Domain Model
ErrorMapper

职责说明:

  • ViewState 描述页面状态,不直接描述网络细节。
  • UseCase 表达业务动作,如刷新新闻、收藏文章、保存设置。
  • Repository 屏蔽数据源,页面不关心远程还是本地。
  • RemoteDataSource 只负责请求和响应 DTO。
  • LocalDataSource 负责 Preferences、文件或数据库。
  • Mapper 把 DTO 转成领域模型。
  • ErrorMapper 把系统错误转成业务错误。

十三、持久化选型

数据 推荐方式 原因 注意
主题、字号 Preferences 小数据、读取快 版本迁移
收藏 id Preferences 或数据库 数据简单 数量增长时迁移
新闻缓存 文件或数据库 数据较多 失效策略
阅读历史 数据库 可查询、可分页 清理策略
草稿 文件/数据库 需要恢复 防损坏
登录态 安全存储能力 敏感 不能明文
图片缓存 文件缓存 体积大 容量上限
配置 Preferences + 远程配置 快速读取 灰度生效

十四、并发与后台任务

场景 推荐策略 风险
首页并行加载 并发请求 错误聚合
图片处理 worker/taskpool 线程通信成本
大列表计算 后台计算 + 分批更新 旧任务覆盖新任务
定时刷新 按需触发 耗电
日志上传 延迟或批量 隐私和流量
离线同步 队列 重复提交
搜索防抖 延迟触发 响应延迟

并发设计要先保证正确性,再考虑速度。所有后台结果回到 UI 前都要校验页面是否仍需要该结果。

十五、架构设计文档模板

text 复制代码
1. 业务目标
2. 页面范围
3. 模块划分
4. 数据模型
5. 状态流
6. 网络和缓存策略
7. 权限和系统能力
8. 错误处理
9. 性能风险
10. 安全和隐私
11. 测试方案
12. 发布和回滚

项目达到一定规模后,每个核心功能都应有轻量设计文档。没有设计文档,后续维护只能靠读代码猜意图。

十六、架构练习套件

练习 目标 验收
抽 Repository 页面不关心数据源 可切换 mock/remote
加 Preferences 设置持久化 重启保留
加数据库设计 阅读历史分页 可查询可删除
加权限服务 统一申请 拒绝可降级
加通知服务 收藏更新提醒 可关闭
加后台队列 日志批量上传 不阻塞 UI
加错误映射 系统错误转业务文案 文案统一
加 traceId 请求链路追踪 日志可关联
加模块边界 feature/common 分层 依赖方向正确
加架构图 说明系统结构 新人能读懂

十七、补充面试题

16. 如何给鸿蒙应用设计数据层?

先定义领域模型,再定义 Repository 接口,然后实现 RemoteDataSource、LocalDataSource 和 Mapper。页面只依赖业务服务,不直接接触 HTTP、Preferences 或数据库。

17. Preferences 和数据库如何选择?

Preferences 适合少量 key-value 数据,如设置和轻量状态;数据库适合结构化、可查询、可分页的数据,如阅读历史、离线列表。选择时看数据量、查询方式、更新频率和迁移成本。

18. 系统能力为什么要统一封装?

权限、通知、媒体、文件等系统能力都有失败路径和设备差异。统一封装可以集中处理权限、日志、错误映射和降级,避免页面到处散落系统 API。

19. 如何设计模块依赖方向?

业务 feature 可以依赖 common-ui、common-model、common-data,但 common 不能反向依赖 feature。数据层不能依赖 UI。依赖方向越单向,项目越容易扩展和测试。

20. 架构设计如何避免空谈?

每个架构结论都要落到文件结构、接口定义、数据流、错误处理和测试方式。只画图不落代码约束,就不能指导开发。

相关推荐
Andya_net1 小时前
网络安全 | 浅析跨网访问对WAF防护架构的影响:网络流向与延迟对比
网络·web安全·架构
●VON2 小时前
鸿蒙Widget开发实战:3张卡片实现桌面-App全链路同步
华为·app·harmonyos·鸿蒙·von
im_AMBER2 小时前
Browser Agent 开发:从浏览器插件到Electron CDP
前端·javascript·架构·electron·agent
我滴老baby2 小时前
企业级工具链设计从单一工具到分层工具体系的架构实践
java·开发语言·架构
陈天伟教授2 小时前
图解人工智能(2)最智能
人工智能·安全·架构
折哥的程序人生 · 物流技术专研2 小时前
出版社物流WMS智能调度实战:从架构升级到机器学习落地
人工智能·机器学习·架构·人机交互
key_3_feng2 小时前
Pura X Max 鸿蒙深度优化方案
华为·鸿蒙
#山间清泉#3 小时前
VMWare虚拟机mac地址自定义配置
运维·macos·架构·vmware
湖南天硕国产SSD3 小时前
SSD主控架构到工业存储落地:天硕自研主控技术路径参照
架构·固态硬盘·天硕存储·ssd固态硬盘