摘要

当我第一次把一个课程表页面跑在鸿蒙设备上时,真正让我兴奋的并不是“页面终于显示出来了”,而是我开始意识到:同一份校园服务,不必再被锁在一块手机屏幕里。课前提醒可以出现在系统级实时信息入口,签到状态可以在不同终端之间自然衔接,平板适合处理长表单,穿戴设备适合承载轻量提醒,服务卡片适合把高频信息推到用户抬手可见的位置。HarmonyOS 所强调的全场景,并不是把同一个界面机械复制到更多设备,而是让任务在最合适的设备、最合适的时间,以最低的交互成本完成。

本文选择“智慧校园”作为贯穿案例,以 Stage 模型、ArkTS、ArkUI、Navigation、状态管理、Preferences 轻量持久化、响应式布局和工程化分层为基础,讨论 HarmonyOS 6.1 相关体验能力如何进入真实业务设计,并对 HarmonyOS 7(API 26)Developer Beta 阶段公开信息进行克制的前瞻分析。文章不会为了显得新而堆叠名词,也不会用无法验证的接口制造“已完成适配”的假象。相反,我会把需求拆解、数据建模、页面状态、路由栈、错误处理、性能分析、测试矩阵和升级策略逐项展开,同时保留开发过程中那些不那么漂亮、却更有价值的判断与返工。

这既是一篇面向开发者的技术实践,也是一份毕业季的学习记录。我希望它能回答三个问题:为什么 HarmonyOS 的开发范式值得重新学习;HarmonyOS 6.1 的体验演进如何转化为产品收益;面对 HarmonyOS 7 的新阶段,开发者应当怎样在“追新”和“稳定交付”之间保持专业判断。

关键词:HarmonyOS 6.1;HarmonyOS 7;API 26;ArkTS;ArkUI;Stage 模型;Navigation;智慧校园;一次开发多端部署;工程化;性能优化

一、写在前面:毕业季,我为什么重新理解应用开发

毕业季总会把很多问题推到眼前:简历上的项目是否真的经得起追问,熟悉一门语言是否等于具备工程能力,完成一个页面是否就算掌握了一个平台。最早学习鸿蒙时,我也把目标设得很窄——能运行、能跳转、能保存数据,就觉得项目已经“做完”。后来参加课程设计、团队评审和真机联调,我才发现平台真正考验的并不是记住多少 API,而是能否把系统能力翻译成用户价值,并在版本变化、设备差异和交付期限之间做出稳定选择。

智慧校园是我刻意挑选的案例。它离学生足够近,又比普通待办应用复杂得多:课程、考试、签到、场地、校园通知、社团活动、失物招领、图书借阅,每一种业务都有不同的时效性、隐私等级和交互频率。它天然需要考虑手机、平板、穿戴设备和系统入口之间的职责分配,也天然适合讨论实时状态、轻量服务、响应式布局和多端协同。

我不想把这篇文章写成“新建工程—复制代码—运行成功”的流水账。真实开发并不是一条直线。需求会变化,官方文档会更新,Beta 能力可能调整,某些看起来漂亮的方案会在无障碍、大字体或分屏模式下暴露问题。与其把过程包装得毫无波折,不如把那些犹豫、失败和重新阅读文档后的修正写清楚。技术文章真正有用的地方,常常不是告诉读者我做对了什么,而是帮助读者少走一段我已经走过的弯路。

Android 或 Web 转向 ArkTS 时,语法并不是最大的门槛。更难的是改变思考方式:不再把 UI 当作一组等待命令式修改的控件,而把它看成状态的函数;不再让页面承担网络、存储、业务、路由全部职责,而让边界明确的模型、服务和状态容器协作;不再以某一块屏幕为唯一中心,而从任务连续性出发规划入口和反馈。

这篇文章因此有两条主线。一条是技术主线,从 Stage 模型、ArkUI、Navigation 到持久化、性能与测试;另一条是体验主线,从校园用户的一次真实任务出发,判断哪些信息该留在应用内,哪些应该被系统级入口承载,哪些能力适合尝鲜,哪些必须等待稳定版本。两条线交叉的地方,就是我对鸿蒙开发范式的理解。

1.1 先澄清版本边界:稳定能力、Beta 能力与产品承诺

截至本文写作时,HarmonyOS 6.1.0 对应 API 23,HarmonyOS 6.1.1 对应 API 24;HarmonyOS 7 的 API 26 处于 Developer Beta 公开体验阶段。对开发者来说,这不是版本号上的细枝末节,而是决定文章和项目能否可信的第一条边界。稳定版本能力可以进入正式交付计划,Beta 能力可以用于探索、兼容验证和技术储备,却不应直接写成“所有设备均已支持”或“线上项目已经稳定使用”。

内容类型

本文处理方式

项目决策

HarmonyOS 6.1 稳定公开能力

结合版本说明和正式文档展开

可进入主干方案,但仍做设备与版本检测

HarmonyOS 7 / API 26 Beta

只讨论官方公开方向与实验性验证

放入实验分支,不作为唯一业务路径

未找到正式文档的接口或行为

不写具体调用,不做确定性描述

保留占位需求,等待官方文档

示例代码

说明架构与关键机制

在目标 SDK 和真机上编译、测试后再提交

这个边界看起来保守,却是专业开发最重要的品质之一。越是热点版本,越容易出现二手文章把概念、演示和可调用 API 混在一起。真正进入团队协作以后,一句未经验证的“官方已经支持”会给测试、产品和上线排期带来连锁成本。

1.2 会写页面能交付场景

智慧校园首页不是功能入口的陈列柜。学生打开它,通常带着具体意图:下一节课在哪里、签到还有多久结束、图书什么时候到期、临时调课是否影响今天安排。页面设计应当围绕这些高频任务排序,而不是围绕后端有多少张表排序。

我把首页第一屏定义为“今天”,而不是“全部功能”。它展示下一课程、进行中的事项、待确认通知和快捷服务。课程详情、活动报名和借阅记录仍然存在,但它们被放到符合使用频率的位置。这个选择直接影响数据加载策略:首屏只请求今天必要的数据,低频模块延迟加载;实时状态单独维护,不因为用户切换普通页面而被销毁。

当需求被改写成用户任务以后,系统能力的价值就更清楚了。临近上课的提醒适合轻量触达,正在进行的签到适合持续状态展示,复杂的选课表单适合大屏或平板,临时通知适合卡片化摘要。这里并不要求每个能力都被接入,而要求开发者能够说明为什么接入、失败时如何降级、用户关闭权限后是否仍能完成核心任务。

二、开发范式演进:StageArkTS ArkUI 如何共同改变工程结构

2.1 Stage 模型不是换一个 Ability 名称

官方将 Stage 模型定位为长期演进的主推应用模型。对复杂应用而言,它的意义不在于把旧生命周期接口重新命名,而在于为应用组件、窗口和共享运行环境建立更清晰的层次。多个应用组件可以共享同一个 ArkTS 引擎实例,适合在边界明确的前提下共享对象和状态,也有利于控制复杂应用的内存开销。

在智慧校园项目里,我把 UIAbility 视为应用界面运行与窗口管理的宿主,把页面视为业务状态的展示,把长期业务状态交给独立状态层。课程列表不应因为某个页面销毁就失去数据,签到倒计时也不应依赖某个 Text 组件仍然存在。生命周期回调只负责“何时建立和释放连接”,业务状态本身必须能被重新构建。

我曾经把网络初始化、用户资料读取、路由参数解析和统计埋点全部塞进页面出现回调。结果是页面每次返回都会重复请求,弱网环境下闪烁明显,调试日志也很难分辨到底是谁触发了刷新。后来把冷启动初始化、前台恢复刷新和单页面参数加载拆开,问题才真正消失。生命周期并不是越集中越好,而是每一层只管理它拥有的资源。

import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';

export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage): void {
    // 这里只负责建立窗口与加载入口页面。
    // 用户会话、课程数据与通知状态由独立服务初始化。
    windowStage.loadContent('pages/Index');
  }

  onForeground(): void {
    // 前台恢复时触发轻量刷新信号,
    // 避免在 Ability 生命周期中直接编排所有业务请求。
  }

  onBackground(): void {
    // 暂停无需后台持续执行的任务,并持久化必要状态。
  }
}

2.2 ArkTS 的约束,换来的是更早暴露问题

ArkTS 保留 TypeScript 的基本语法风格,同时通过更严格的静态检查提高代码健壮性。初学时,这些约束会让人觉得“不够自由”:一些模糊类型、动态属性或随手拼装对象的写法不再顺畅。但当项目进入多人维护阶段,明确的数据结构、可推导的类型和稳定的对象边界,会明显减少运行时才出现的错误。

课程数据是一个典型例子。后端字段可能为空,课程地点可能临时调整,教师信息可能只返回部分内容。与其在 UI 中到处写空值判断,我更愿意在数据适配层把远端结构转换为应用内部稳定模型。页面拿到的 CourseViewData 应该已经具备可直接展示的标题、时间文本和状态枚举。

这也意味着接口模型与页面模型不必完全相同。远端对象服务于传输,领域对象服务于业务规则,视图对象服务于展示。把三者强行合并看似少写了几个类,实际会让后端变化直接穿透整个页面。

export enum CourseStatus {
  Upcoming = 'upcoming',
  Ongoing = 'ongoing',
  Finished = 'finished',
  Changed = 'changed'
}

export interface CourseRecord {
  id: string;
  name: string;
  teacher?: string;
  room?: string;
  startAt: number;
  endAt: number;
  status: CourseStatus;
}

export interface CourseCardViewData {
  id: string;
  title: string;
  subtitle: string;
  timeText: string;
  statusText: string;
  emphasized: boolean;
}

2.3 声明式 UI:页面描述结果,事件改变状态

ArkUI 是声明式 UI 框架。真正适应它以后,页面代码会从“找到组件并修改属性”转向“当前状态下应该显示什么”。这不是语法美化,而是把界面一致性问题交给状态关系处理。

智慧校园首页有加载中、正常、空数据、部分失败和离线缓存五种状态。命令式写法很容易在多个回调里分别控制组件显示隐藏,最后出现骨架屏已经消失、错误提示仍在、旧列表又被刷新出来的混合状态。声明式页面则可以先把状态机写清楚,再由 build 根据状态选择唯一分支。

状态驱动并不意味着把所有变量都标成响应式。过度响应会扩大刷新范围,甚至让一个倒计时引起整个首页重建。我的原则是:只有会影响当前 UI 的最小数据才进入观察体系,计算结果优先派生,跨页面长期状态集中管理,临时变量保持普通属性。

enum LoadState {
  Loading,
  Ready,
  Empty,
  PartialError,
  Offline
}

@Entry
@Component
struct TodayPage {
  @State private loadState: LoadState = LoadState.Loading;
  @State private courses: CourseCardViewData[] = [];

  build() {
    Column() {
      if (this.loadState === LoadState.Loading) {
        LoadingProgress()
      } else if (this.loadState === LoadState.Empty) {
        EmptyCampusView()
      } else {
        CourseList({ courses: this.courses })
        if (this.loadState === LoadState.PartialError) {
          Text('部分信息暂未更新,已显示可用内容')
        }
      }
    }
  }
}

2.4 Navigation:把路由当作状态,而不是一次性命令

Navigation 是官方长期演进和推荐的路由方案。它提供导航容器、NavDestination 页面和导航栈管理,适合构建单栏、分栏以及多设备变化下的统一导航结构。相比只会“跳到另一个地址”的字符串路由,Navigation 更强调栈状态和页面组织。

智慧校园在手机上采用单栏路径:首页进入课程详情,再进入教室地图;在平板横屏上,课程列表与详情可以同时出现。业务页面不应知道自己一定占满屏幕,它只声明内容和操作,外层导航根据断点选择单栏或分栏。这样做的成本是前期结构稍复杂,收益是不用维护两套完全独立的页面。

路由参数也必须克制。不要把完整课程对象序列化塞进路由,而应传递稳定标识,让目标页面从统一状态层读取最新数据。否则列表页更新后,详情页仍可能展示进入时的旧对象,形成难以察觉的数据分叉。

三、智慧校园项目设计:从用户旅程到可维护架构

3.1 核心用户旅程

旅程

关键动作

体验目标

失败降级

上课前

查看下一课程、教室、路线

一眼获得必要信息

展示缓存课表并标记更新时间

课堂签到

进入签到、确认状态、查看剩余时间

低操作成本、状态连续

应用内保留完整入口与状态查询

临时调课

接收变更、确认影响

信息明确且可追溯

通知中心与应用消息中心双路径

图书到期

查看借阅与续借条件

避免遗漏,减少跳转

离线显示到期日期,联网后执行续借

活动报名

浏览、填写、提交、查看结果

表单稳定、错误可恢复

草稿本地保存,提交失败可重试

 

3.2 工程目录与职责边界

entry/src/main/ets/
├── ability/
│   └── EntryAbility.ets
├── pages/
│   ├── Index.ets
│   ├── TodayPage.ets
│   ├── CourseDetailPage.ets
│   ├── CheckInPage.ets
│   └── ProfilePage.ets
├── components/
│   ├── CourseCard.ets
│   ├── StatusBanner.ets
│   ├── EmptyCampusView.ets
│   └── ResponsiveShell.ets
├── domain/
│   ├── model/
│   ├── repository/
│   └── usecase/
├── data/
│   ├── remote/
│   ├── local/
│   └── mapper/
├── state/
│   ├── SessionStore.ets
│   ├── TodayStore.ets
│   └── CheckInStore.ets
├── navigation/
│   └── CampusNavPath.ets
├── common/
│   ├── constants/
│   ├── error/
│   └── utils/
└── resources/

这个目录不是为了显得“企业级”,而是为了让变化停留在应该变化的地方。后端字段变化主要影响 data/mapper,课程规则变化主要影响 domain,页面视觉调整主要影响 components 和 pages。任何目录结构只要不能降低修改影响范围,就是形式主义。

Repository 负责隐藏数据来源。今天课程可能来自网络、缓存或本地模拟数据,页面不应该根据网络状态分别调用三个服务。UseCase 表达业务动作,例如“加载今天摘要”“提交签到”“续借图书”。Store 保存当前界面真正需要观察的状态。这样的分层让单元测试可以绕开 UI,也让 Beta 能力的接入保持在边缘层。

我特别避免建立一个无所不包的 GlobalStore。全局状态越多,页面之间的隐式依赖越强。会话信息、主题偏好可以全局共享;签到倒计时只需要在签到业务范围内共享;课程详情的展开状态只属于当前页面。状态的作用域越准确,刷新和调试越容易。

3.3 数据状态模型:成功之外还有部分成功

校园首页经常同时请求课程、通知、借阅和活动。如果把它们捆绑成一个 Promise.all,只要一个接口失败,整页就可能进入错误状态。用户明明还能看到课程,却被迫面对“加载失败”。更合理的模型是允许部分成功:关键模块优先显示,失败模块独立重试,页面顶部以低打扰方式说明数据完整性。

我为每个模块保存 data、loading、error、updatedAt 四类信息,再由页面派生整体状态。离线时先展示本地缓存,并把“最近更新于 08:10”明确写出来。缓存不是伪装成实时数据,而是在可用性与真实性之间建立透明关系。

错误也应分层。网络不可用、登录过期、服务器业务拒绝、数据解析失败,对用户和开发者意味着不同动作。页面显示的人类语言与日志记录的技术上下文应当分离,既避免把堆栈暴露给用户,也保留排查所需信息。

export interface ModuleState<T> {
  data?: T;
  loading: boolean;
  error?: CampusError;
  updatedAt?: number;
}

export class CampusError {
  constructor(
    public readonly code: string,
    public readonly userMessage: string,
    public readonly retryable: boolean,
    public readonly cause?: Error
  ) {}
}

3.4 Preferences 适合保存什么,不适合保存什么

Preferences 适合轻量键值数据,例如主题选择、首页排序、上次选中的校区、筛选条件和小规模缓存索引。它的优势是接口简单、读取成本低,但并不意味着所有数据都应该被 JSON 化后塞进去。

课程历史、消息列表或报名草稿如果持续增长,需要更适合结构化查询和事务的数据方案。安全敏感信息也不能因为 Preferences 使用方便就直接明文保存。选择存储技术时应当从数据规模、查询方式、生命周期、敏感等级和迁移策略出发,而不是从“哪段示例代码最短”出发。

我把用户偏好与业务缓存分开命名和版本化。每次模型升级时,先读取 schemaVersion,再决定迁移或清理。没有版本的本地数据,往往会在应用升级后变成最难复现的线上问题。

四、HarmonyOS 6.1 体验能力:不为接入而接入

4.1 沉浸光感与悬浮页签:先理解视觉层级,再谈组件

HarmonyOS 6.0/6.1 新能力页面将沉浸光感组件描述为把悬浮页签与沉浸光感应用于标题栏和底部导航,通过材质、交互与自适应设计提升核心界面体验。设计指南强调光线、材质与层级之间的关系。对开发者而言,它不是简单加一层半透明背景,也不是把所有导航都做成漂浮胶囊。

智慧校园首页信息密度高:课程卡片、通知、状态条和底部导航同时存在。若每一层都强调光感,页面会失去主次。我只在核心导航和需要与滚动内容分离的浮层使用相应视觉语言,并保证文字对比度、焦点状态和深浅色模式下的可读性。

悬浮页签适合在有限数量、互斥且高频的内容分类之间切换,例如“今天、课程、校园”。它不适合承载十几个后台栏目,也不适合替代完整信息架构。页签切换应保留用户上下文:滚动位置、筛选条件和已加载数据不应全部重置。

实现层面要把设计效果与具体 SDK 支持分开。项目应以目标 API 版本的官方组件和属性为准,对不支持的设备提供普通标题栏或底部导航降级。视觉升级不能成为核心功能不可用的理由。

4.2 实时状态入口:以签到为例设计开始更新结束

活动要求中提到实况窗,但技术文章不能只写“接入后体验更好”。真正需要设计的是状态生命周期。课堂签到从未开始、可签到、已提交、教师确认、结束或异常终止,每一种状态都有明确的展示内容、更新时间和结束条件。

持续状态信息必须克制。用户真正需要的是课程名称、剩余时间、签到结果和必要动作,而不是把应用详情页压缩到系统入口。每次更新都应有业务意义,避免高频刷新造成资源消耗和注意力干扰。

应用被系统终止、网络切换、用户退出账号、课程被取消时,状态入口如何结束,是比“如何开始”更重要的问题。我的实现会把状态源放在业务服务中,系统入口只是订阅和呈现;同时保留应用内的完整状态页面,确保入口不可用或权限关闭时,核心任务仍可完成。

由于具体开放能力、接口名称和适用条件会随 SDK 与审核规则变化,正式接入必须逐项对照目标版本官方文档。本文不提供未经当前项目环境编译验证的实况窗接口伪代码,而提供可以长期复用的状态建模方法。

签到状态

用户可见信息

允许操作

终止条件

未开始

课程与开始时间

查看详情

进入可签到时间窗

可签到

剩余时间、地点校验状态

立即签到

提交成功或时间窗结束

处理中

已提交,等待确认

刷新状态

教师确认或服务返回失败

成功

签到成功时间

查看记录

课程结束后归档

失败

失败原因与重试提示

重试/进入应用

重试成功或时间窗结束

已结束

签到已结束

查看记录

系统入口清理

 

4.3 智感握姿、碰一碰与应用接续:校园场景中的取舍

HarmonyOS 6.0/6.1 新能力介绍还包含智感握姿、碰一碰、应用接续、跨设备互通等方向。它们很适合说明鸿蒙的场景化价值,但每一种能力都需要硬件、系统版本、权限和产品流程共同成立。

智感握姿可以帮助高频单手操作场景调整界面,例如校园地图或扫码页面将主要操作放到更易触达区域。但布局变化必须稳定、可预测,不能因为握姿识别轻微波动让按钮不断跳动。更合理的方式是设置状态滞后、限制调整范围,并提供固定布局选项。

碰一碰适合分享活动邀请、组队信息或临时资料。设计时要避免把敏感身份信息直接放入分享载荷,接收端应验证数据有效期和权限。分享动作只是任务入口,不应绕过业务授权。

应用接续最能体现任务连续性。学生可以在手机上浏览课程资料,在平板上继续填写复杂实验报告。要实现自然接续,页面状态必须可序列化、可恢复,未提交内容要有明确版本,并处理两端同时编辑的冲突。单纯把目标页面打开,并不等于真正完成接续。

4.4 6.0 升级到 6.1:我的适配顺序

  1. 锁定基线:记录当前 DevEco Studio、SDK、插件、三方依赖和签名配置,保留可回退分支。
  2. 阅读版本概览、OS 新增与增强特性、API 变更清单和行为变更说明,不只看营销特性页面。
  3. 先解决编译与静态检查问题,再处理运行时行为,不在同一提交里同时重构业务。
  4. 建立覆盖冷启动、前后台切换、分屏、旋转、深浅色、大字体、弱网与无网的回归矩阵。
  5. 新体验能力使用特性开关和版本判断,小范围验证后再扩大;核心流程始终保留稳定降级。
  6. 用性能工具比较升级前后启动、帧率、内存和网络请求,避免“编译通过”被误认为“升级完成”。

我最深的一次教训,是在升级后只验证了首页和登录,忽略了从后台恢复的签到页面。页面重新可见时启动了第二个倒计时,两个定时任务同时更新状态,偶发出现时间跳动。它提醒我:版本适配的风险通常藏在非主路径和生命周期交界处。

 

 

五、关键页面实战:状态、路由、响应式与可测试性

5.1 “今天首页:以最小刷新范围组织组件

首页由顶部摘要、下一课程、进行中事项、校园通知和底部导航组成。我不让整个页面订阅一个巨大的对象,而把每个模块拆成独立、只接收必要数据的组件。下一课程倒计时每分钟变化,只更新对应卡片;通知未读数变化,不应触发课程列表重建。

组件拆分不等于把每十行代码放进一个文件。拆分依据应是职责、复用和刷新边界。CourseCard 负责展示课程及发出点击事件,不直接访问 Repository;TodayPage 负责布局模块,不直接解析网络对象;TodayStore 负责编排加载并发布 UI 状态。

列表项必须有稳定标识。使用数组下标作为 key 会在插入或排序时导致组件状态错位。课程 id、通知 id 才是可靠身份。对于长列表,使用懒加载容器并避免在 item 构建中执行昂贵计算。

@Component
struct CourseCard {
  @Prop data: CourseCardViewData;
  onOpen: (id: string) => void = () => {};

  build() {
    Row() {
      Column({ space: 4 }) {
        Text(this.data.title)
          .fontWeight(FontWeight.Medium)
        Text(this.data.subtitle)
          .fontSize(12)
        Text(this.data.timeText)
          .fontSize(12)
      }
      .alignItems(HorizontalAlign.Start)
      .layoutWeight(1)

      Text(this.data.statusText)
    }
    .padding(16)
    .onClick(() => this.onOpen(this.data.id))
  }
}

5.2 响应式布局:多端不是按比例放大

手机竖屏适合单栏滚动,平板横屏适合主从结构,折叠屏展开态可以在不牺牲阅读宽度的前提下展示更多上下文。一次开发多端部署的目标是复用业务和大部分组件,而不是让同一个固定宽度页面被强行拉伸。

我把断点决策集中在 ResponsiveShell。页面内容组件只声明“列表区域”和“详情区域”,外壳根据可用宽度决定叠放或并排。这样课程详情在手机上作为 NavDestination 打开,在宽屏上可以作为右侧面板显示,业务组件无需维护两个版本。

响应式还包括输入方式差异。触摸设备强调触控目标尺寸,键鼠环境需要悬停、焦点和快捷键,穿戴设备则要求更少层级和更短文本。所谓多端一致,不是视觉完全相同,而是任务和信息保持一致,交互符合设备习惯。

5.3 表单草稿:活动报名不应因一次返回而丢失

活动报名页面包含联系人、学院、说明和附件。用户可能切到其他应用查找信息,也可能因为网络失败暂时退出。若所有内容只保存在组件 @State 中,页面重建后就会丢失。

我把表单字段保存在页面级 ViewModel,并在字段变化后做节流持久化。草稿保存显示明确状态:已保存、本地待同步、提交中、提交失败。用户再次进入同一活动时,可以选择恢复或放弃草稿。

提交操作必须幂等。客户端生成 requestId,重复点击或弱网重试不会创建多条报名。按钮禁用只能减少误触,不能替代服务端幂等。

附件上传与表单提交分离,页面展示每个文件的进度和失败原因。任何长任务都需要取消与恢复策略,不能只展示一个永远转动的加载圈。

5.4 可测试性:把时间和网络变成可替换依赖

签到和课程状态高度依赖时间。若业务代码直接随处调用系统当前时间,边界条件很难测试。引入 Clock 接口后,测试可以固定在签到开始前一分钟、结束时刻和跨日场景。

Repository 使用接口,使单元测试能够返回成功、超时、部分数据和过期缓存。页面测试不必依赖真实校园服务器。可测试性不是测试团队的附加要求,而是架构是否清晰的直接反馈。

我会优先测试状态转换,而不是截图式验证每一个像素。比如 loadToday 在课程成功、通知失败时是否进入 PartialError;提交签到超时后是否允许重试;退出登录后是否清理用户级缓存和持续状态入口。

六、性能、稳定性与隐私:真正决定上线质量的部分

6.1 启动性能:首屏只做必要工作

启动阶段最容易被“顺便做一下”的任务拖慢:初始化所有 SDK、预加载全部字典、读取大量缓存、请求四五个接口。智慧校园首屏只需要用户会话、今天摘要和基础主题。其他模块可以在首帧之后或进入页面时按需加载。

我把启动任务分为阻塞、首屏后和按需三组。阻塞任务必须能够解释为什么不完成就不能展示第一屏;首屏后任务通过调度延后;低频模块直到用户进入才初始化。这个分类比盲目并发更有效,因为并发任务仍会争夺 CPU、IO 和网络。

性能优化应先测量。启动耗时、页面帧率、内存快照和网络瀑布图提供事实,肉眼“感觉挺快”不能作为结论。每次版本升级和引入新能力后,都保留基线对比。

6.2 列表与状态刷新:避免隐性全量重建

课程表和消息列表数据量不算巨大,却很容易因错误状态设计产生卡顿。常见问题包括:每次倒计时都创建全新数组;在 build 中排序和格式化全部日期;父组件状态变化导致所有 item 重新计算;图片没有合理尺寸与缓存。

解决顺序是先稳定数据身份,再缩小观察范围,然后把昂贵计算移出构建路径。格式化后的时间文本在 mapper 中生成,只有源数据变化时更新;筛选结果使用可控的派生逻辑;列表采用稳定 key。

不要为了“减少刷新”而把状态藏起来。正确做法不是放弃响应式,而是让响应式数据足够小、语义清楚。

6.3 定时器、监听器与资源释放

倒计时、网络监听、剪贴板监听和窗口事件都可能成为泄漏来源。创建它们时就应决定谁负责释放。页面临时监听由页面生命周期管理,业务级持续任务由对应服务管理,不能依赖垃圾回收替代显式解绑。

前后台切换时不需要继续逐秒更新的 UI 定时器应暂停,恢复时根据真实时间重新计算,而不是补跑所有丢失 tick。这样既节省资源,也避免系统挂起后计时漂移。

我在开发环境为监听注册和释放增加计数日志。页面退出后计数未归零,就说明资源边界有问题。这种简单机制比线上偶发内存增长更容易定位。

6.4 隐私与校园身份:最小化收集不是一句口号

校园应用会接触学号、课程、签到位置、借阅记录等敏感信息。设计时应先问“完成任务是否真的需要这项数据”,再问“如何申请权限”。签到需要位置时,应在动作发生前解释用途,并在拒绝后提供可理解的替代流程或人工申诉,而不是让应用停在空白页。

日志不能记录完整学号、令牌、精确位置或表单内容。测试环境的便利输出很容易被带入生产。崩溃上报也要经过字段清理。

本地缓存按用户隔离,退出登录时清理用户级数据。截图、剪贴板和分享场景要考虑信息泄露。任何智能能力或 Agent 能力接入,都应明确数据输入、处理位置和用户授权,不因“智能化”跳过基本隐私原则。

6.5 测试矩阵:不是设备越多越好,而是风险覆盖完整

维度

至少覆盖

重点观察

系统/API

项目最低支持版本、6.1 主版本、目标新版本/Beta

API 可用性、行为变化、降级路径

设备形态

手机、平板/宽屏、折叠态或模拟断点

布局、导航模式、输入方式

显示

浅色、深色、大字体、高对比需求

文字截断、对比度、焦点

网络

正常、慢网、断网、恢复、请求超时

缓存透明度、重试、幂等

生命周期

冷启动、热启动、后台恢复、系统回收后重建

重复请求、状态恢复、资源释放

账号

登录、过期、退出、切换用户

缓存隔离、路由回退、持续状态清理

时间

跨日、时区、签到边界、夏令时环境测试

课程归属、倒计时、过期判断

 

七、HarmonyOS 7API 26)前瞻:保持期待,也保持工程克制

HarmonyOS 7(API 26)Developer Beta 已向开发者开放体验和兼容性适配验证。官方版本入口和开发者招募信息说明,API 26 的价值首先是让开发者提前验证新能力、工具链和应用兼容性,而不是要求所有正式项目立刻迁移。

面对新版本,我最关心的不是“又增加了多少 API”,而是三个问题:现有工程是否能更稳定地表达复杂场景;跨设备任务是否能以更低成本延续;AI 与系统能力开放是否形成可测试、可治理的工程链路。只有当新能力能够进入需求、架构、测试和降级体系,它才真正属于项目。

智慧校园可以为 API 26 建立独立实验分支。实验内容包括编译兼容性、关键页面行为、性能基线和新能力原型。主干仍以稳定 SDK 为准。每一个实验都记录前置条件、设备范围、权限、失败表现和回退方案,避免演示成功一次就被误判为可上线。

对文章写作也应保持同样克制。前瞻可以讨论官方公开方向、版本页面和 Beta 体验,却不能虚构类名、参数和上线时间。技术可信度比“抢先”更重要。

7.1 我认为开发者应提前准备的四种能力

  • 版本阅读能力:能从版本概览、API 变更、行为变更和设计指南中找到真正影响项目的内容。
  • 跨端架构能力:业务模型、状态和页面能够在不同窗口与设备形态下复用,而不是复制工程。
  • 智能能力工程化:不仅会调用模型,还能处理权限、数据边界、工具调用、失败降级与可观测性。
  • 体验验证能力:把视觉、性能、无障碍、隐私和稳定性放在同一张交付清单中。

7.2 不追逐版本号,追逐可迁移的工程资产

一个项目真正有价值的资产,不是某个版本专用的炫酷页面,而是稳定的数据模型、清晰的状态机、可替换的数据源、响应式布局壳、自动化测试和版本适配清单。这些资产能跨版本迁移,也能让新能力接入不破坏核心流程。

API 26 稳定后,智慧校园需要做的不是推倒重来,而是在已有边界中替换或新增适配器。新导航能力进入 navigation 层,新系统入口进入 integration 层,新智能服务通过 usecase 暴露给页面。架构边界为创新提供的是安全空间,而不是阻力。

八、踩坑记录:那些真正改变我写法的问题

8.1 页面返回后重复请求

我最初在页面每次可见时无条件加载全部数据。课程详情返回首页后,网络请求和骨架屏重新出现,用户觉得页面“闪了一下”。解决方法不是简单加一个布尔值,而是定义数据新鲜度:首次进入加载、超过阈值后台刷新、用户主动下拉强制刷新。已有数据时不清空页面,失败只提示刷新未完成。

复盘时我会把问题写成五列:触发条件、用户表现、技术根因、修复方式、回归用例。只记录“已修复”没有复用价值;能够被下一次版本升级直接使用的回归用例,才算真正沉淀。

8.2 把完整对象塞进路由参数

列表到详情传递完整课程对象,看起来省掉一次查询。课程被临时调课后,列表更新而详情仍保存旧对象。后来只传课程 id,并由详情订阅统一 Store;路由参数只承担定位,不承担数据快照。

复盘时我会把问题写成五列:触发条件、用户表现、技术根因、修复方式、回归用例。只记录“已修复”没有复用价值;能够被下一次版本升级直接使用的回归用例,才算真正沉淀。

8.3 倒计时引发全页刷新

首页 Store 保存当前秒数,所有订阅者每秒重建。改为签到卡片内部根据结束时间计算显示值,并把刷新粒度降到业务需要的分钟或秒;其他模块不订阅计时状态。

复盘时我会把问题写成五列:触发条件、用户表现、技术根因、修复方式、回归用例。只记录“已修复”没有复用价值;能够被下一次版本升级直接使用的回归用例,才算真正沉淀。

8.4 缓存数据假装实时

离线时直接展示缓存,没有更新时间。用户误以为临时调课仍未发生。后来所有缓存视图都显示更新时间,并在关键状态上提供显眼但不阻断的离线提示。

复盘时我会把问题写成五列:触发条件、用户表现、技术根因、修复方式、回归用例。只记录“已修复”没有复用价值;能够被下一次版本升级直接使用的回归用例,才算真正沉淀。

8.5 深色模式只改背景

背景变暗后,一些次要文字和分割线对比度不足,悬浮层材质也显得浑浊。修正时不再逐个写死颜色,而使用语义色和统一主题令牌,并在真实设备亮度范围下检查。

复盘时我会把问题写成五列:触发条件、用户表现、技术根因、修复方式、回归用例。只记录“已修复”没有复用价值;能够被下一次版本升级直接使用的回归用例,才算真正沉淀。

8.6 大字体破坏按钮布局

按钮使用固定高度和单行文本,字体放大后文字被裁切。改为保证最小触控区域、允许合理换行或缩短文案,并把重要操作从纯图标改为图标加可访问标签。

复盘时我会把问题写成五列:触发条件、用户表现、技术根因、修复方式、回归用例。只记录“已修复”没有复用价值;能够被下一次版本升级直接使用的回归用例,才算真正沉淀。

8.7 监听器未释放

页面反复进入后网络状态回调执行多次。问题根因是注册与释放不在同一所有者中。后来封装订阅句柄,并在生命周期结束时统一 dispose。

复盘时我会把问题写成五列:触发条件、用户表现、技术根因、修复方式、回归用例。只记录“已修复”没有复用价值;能够被下一次版本升级直接使用的回归用例,才算真正沉淀。

8.8 Beta 能力直接进入主流程

原型演示效果很好,团队一度想把它作为唯一入口。设备覆盖和审核条件尚未确认。最终使用特性开关和稳定替代路径,Beta 只进入灰度实验。

复盘时我会把问题写成五列:触发条件、用户表现、技术根因、修复方式、回归用例。只记录“已修复”没有复用价值;能够被下一次版本升级直接使用的回归用例,才算真正沉淀。

九、毕业季·鸿蒙同行:从课程作业到职业作品

毕业设计最容易陷入“功能数量竞赛”:登录、列表、搜索、聊天、地图、AI 全部都要有,最后每个功能只做到能截图。真正能在面试中展开的项目,通常有清楚的问题定义、合理的技术选择和一两个做深的难点。

智慧校园项目不需要连接真实学校系统才能成为有价值的作品。可以使用合规模拟数据,把重点放在状态机、离线策略、响应式布局、测试和性能上。面试官追问“为什么这样设计”时,能够说明权衡,比展示几十个页面更有说服力。

我建议为项目保留四份材料:架构说明、关键决策记录、测试报告和迭代日志。README 不只是启动步骤,还应说明支持版本、模块边界、已知限制和后续计划。每一次官方版本升级,都可以形成一篇真实的适配记录。

学习路线也不必追求把所有 Kit 一次学完。先掌握 ArkTS 基础、ArkUI 状态与布局、Stage 生命周期、Navigation、网络和持久化,再用一个项目反复迭代。遇到问题优先回到官方文档、版本说明和示例,二手文章用于启发,不用于替代依据。

我在鸿蒙学习中最大的变化,是不再把“运行成功”当作终点。一个能恢复、能降级、能解释权限、能在大字体和分屏下工作、能在升级后快速回归的应用,才接近真正的工程交付。这个标准也许让进度看起来慢了一些,却让每一次提交都更有底气。

9.1 一份可执行的十二周学习与项目计划

周次

目标

可交付结果

1-2

ArkTS、ArkUI 基础与官方示例

完成静态校园首页与组件拆分

3

Stage 生命周期与窗口

完成冷启动、前后台日志与资源管理

4

Navigation 与参数设计

手机单栏导航可用,路由只传稳定标识

5

Repository、Store 与错误模型

模拟成功、失败、离线和部分成功

6

Preferences 与草稿

主题、筛选、报名草稿可恢复

7

响应式布局

手机和平板断点布局完成

8

6.1 体验规范

完成导航视觉与交互适配原型

9

性能分析

形成启动、列表和内存基线报告

10

隐私与无障碍

权限说明、大字体、焦点和语义检查

11

API 26 实验分支

完成兼容性记录,不影响稳定主干

12

文档与演示

架构图、测试报告、演示视频和投稿文章

 

十、结语:真正值得期待的,是开发者与生态一起成长

HarmonyOS 6.1 到 HarmonyOS 7,最吸引我的并不是版本号本身,而是应用与系统、设备与任务之间的边界正在被重新组织。悬浮页签和沉浸光感提醒我们,视觉体验必须与层级和交互一致;实时状态入口提醒我们,信息可以离开应用页面,但不能离开完整生命周期;一次开发多端部署提醒我们,复用的应该是任务与业务,而不是固定像素。

智慧校园项目让我重新认识了“简单需求”。一张课程卡片背后有数据新鲜度、时区、调课、离线和无障碍;一次签到背后有权限、时间边界、幂等、持续状态和异常终止;一个多端页面背后有导航模式、输入方式与状态恢复。把这些问题认真解决,远比堆叠概念更接近鸿蒙生态需要的开发能力。

我也愿意承认,很多能力仍需要在具体 SDK、设备和官方规则下继续验证。技术文章不应该掩盖这种不确定性。能够准确区分稳定、Beta 和设想,能够在创新失败时让核心任务继续完成,本身就是工程成熟度。

毕业季意味着离开熟悉的课程表,也意味着开始为自己的选择负责。学习鸿蒙对我而言不再是“多会一门技术”,而是一次重新训练产品思维、系统思维和工程纪律的过程。生态快速变化会带来焦虑,也会给愿意长期积累的人留下位置。

下一次打开 DevEco Studio,我仍然会遇到编译错误、版本变化和没有现成答案的问题。但和最初不同的是,我不再急着证明自己什么都会。我更愿意先把边界查清,把状态画清,把失败路径补清,再写下那行真正能够被用户依赖的代码。

附录 A:官方技术资料索引

资料

官方地址

HarmonyOS 所有开发套件版本

https://developer.huawei.com/consumer/cn/doc/harmonyos-releases/overview-allversion

HarmonyOS 6.1.0(API 23)版本概览

https://developer.huawei.com/consumer/cn/doc/harmonyos-releases/overview-610

HarmonyOS 6.1.1(API 24)版本概览

https://developer.huawei.com/consumer/cn/doc/harmonyos-releases/overview-611

HarmonyOS 6.0/6.1 新能力介绍

https://developer.huawei.com/consumer/cn/features/6-1

沉浸光感设计指南

https://developer.huawei.com/consumer/cn/doc/design-guides/immersivelight-0000002612101053

Stage 模型开发概述

https://developer.huawei.com/consumer/cn/arkui/arkui-stage/

ArkUI 框架介绍

https://developer.huawei.com/consumer/cn/arkui/

Navigation 组件 API 参考

https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-navigation

HarmonyOS 7(API 26)Developer Beta 招募

https://developer.huawei.com/consumer/cn/activity/developerbeta/harmonyos-developer-beta-7-1

HarmonyOS 开发平台入口

https://developer.huawei.com/consumer/cn/develop/

 

说明:官方文档会持续更新。投稿或发布前,请再次打开上述页面核对版本日期、API 等级、设备支持范围与审核要求。

附录 B:智慧校园项目详细回归用例

用例

预期结果

冷启动且网络正常

首页首屏先展示必要骨架,用户会话建立后加载今天摘要;低频模块不阻塞首帧。

冷启动且网络断开

读取缓存,显示更新时间和离线提示;无缓存时展示可理解空态与网络设置入口。

登录令牌过期

停止用户级请求,清理敏感缓存,回到登录;不得出现旧账号信息闪现。

从后台恢复

根据数据新鲜度轻量刷新;倒计时只存在一个;窗口和监听器不重复注册。

系统回收后重建

通过持久化的最小状态和路由标识恢复页面,不依赖内存中的完整对象。

课程临时调换

首页和详情订阅同一数据源;缓存记录版本;旧信息明确标记或被替换。

签到开始边界

服务端时间与客户端显示策略一致;提前进入页面可自动转为可签到状态。

签到结束边界

提交中的请求有确定结果;超时不重复创建记录;入口及时结束。

连续快速点击提交

按钮状态与服务端幂等共同防重;用户看到稳定反馈。

大字体 200%

课程名称、状态和按钮不裁切;操作顺序与语义保持清楚。

深色模式切换

语义色、浮层材质、图标和状态色可读;切换不需要重启页面。

手机横屏/分屏

布局按宽度而非设备名称变化;关键按钮仍可达。

平板宽屏

列表与详情可并排;导航返回逻辑不重复压栈。

慢网加载

已有数据不被清空;局部模块显示更新中;用户可以取消或继续操作。

服务端部分失败

课程成功、通知失败时仍显示课程,错误提示只针对失败模块。

用户退出账号

清理签到持续状态、用户缓存、草稿和监听;公共配置保留。

分享过期活动

接收端验证有效期和身份,提供清楚的失效提示,不崩溃。

Beta 能力不可用

特性开关关闭或设备不支持时自动回退稳定页面。

 

-End-

相关文件下载
3.png
5.58 MB
下载
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐