作者:梅科尔工作室宗月妍

一、开屏页的设计初衷

开屏页(Splash Screen)是应用的"门面",它承担着三个核心职责:

  1. 品牌展示:在短暂的 2-3 秒内展示应用 Logo 和名称,加深用户品牌印象。
  2. 初始化缓冲:为应用的全局状态初始化、数据库加载、网络预请求等提供时间窗口。
  3. 路由分发:根据用户登录状态等条件,决定跳转到登录页还是主页,实现"一步到位"的用户体验。

在咕噜冰箱中,开屏页的实现非常精简——一个居中的 Logo 图标和"咕噜冰箱"文字,配合 3 秒延时后自动路由跳转。但精简不等于简陋,其背后的登录态判断逻辑涉及 HarmonyOS 多层状态管理机制。

二、AppStorage:全局状态的"神经中枢"

HarmonyOS 提供了 AppStorage 作为应用全局的状态容器,它可以在应用的任何页面、任何组件中被读写,是跨页面数据共享的核心机制。在咕噜冰箱中,用户的 unionId(华为账号唯一标识)是判断登录状态的关键字段,它通过 @StorageLink 装饰器与 UI 组件双向绑定:

@StorageLink('unionId') unionId: string = ''

当 unionId 非空时,说明用户已经登录过;为空则说明是未登录状态。这个值在用户成功登录后写入 AppStorage,在应用退出后通过 Preferences 持久化到本地存储。

三、开屏页路由分发逻辑

开屏页的核心逻辑在 onPageShow 生命周期中执行。这里不使用 aboutToAppear 的原因是,onPageShow 在每次页面显示时都会触发(包括从后台切回前台),确保状态判断的时机更加可靠:

onPageShow(): void {
  setTimeout(() => {
    let storedUnionId = AppStorage.get<string>('unionId') || '';
    if (storedUnionId) {
      router.replaceUrl({ url: 'pages/Home' });
    } else {
      router.replaceUrl({ url: 'pages/Login' });
    }
  }, 3000);
}

这里有几个值得注意的设计细节:

3.1 为什么用 replaceUrl 而不是 pushUrl?

router.replaceUrl 会替换当前页面栈顶的页面,而不是压入新页面。这意味着开屏页不会留在路由栈中——用户在主页按返回键不会回到开屏页,这符合用户预期。如果使用 pushUrl,用户在主页按返回会看到开屏页再次等待 3 秒,体验非常糟糕。

3.2 为什么用 3000ms 延时?

3000ms 是一个平衡值:太短则品牌展示不够充分、初始化可能未完成;太长则用户感到等待焦躁。实际开发中,可以考虑用"初始化完成 OR 最短展示时间"的双重条件来替代固定延时,实现更智能的跳转时机。

四、Preferences:让登录态"活过"重启

AppStorage 是内存态的,应用杀进程后就清空了。要实现"重启应用后自动登录"的效果,必须将关键数据持久化到本地。HarmonyOS 提供了 @kit.ArkData 中的 preferences 轻量级键值存储,非常适合这类场景。

咕噜冰箱封装了 PreferencesUtil 单例类来统一管理偏好设置,核心方法包括:

// 获取Preferences实例
getPreferences(context: Context): preferences.Preferences {
  return preferences.getPreferencesSync(context, { name: 'myStore' });
}

// 写入数据并持久化
preferencesPut(prefs: preferences.Preferences, key: string, value: ValueType): void {
  prefs.putSync(key, value);
  prefs.flush(); // 异步刷盘,确保数据写入
}

在用户登录成功后,将 unionId 等关键信息写入 Preferences;在应用启动时(EntryAbility.onCreate),从 Preferences 读取并回填到 AppStorage,从而让开屏页能够正确判断登录状态。

五、PersistentStorage:声明式持久化方案

除了手动使用 Preferences 外,HarmonyOS 还提供了 PersistentStorage,它是一种声明式的持久化方案,可以将 AppStorage 中的指定属性自动同步到持久化存储中:

PersistentStorage.persistProp('unionId', '');

这行代码的含义是:将 AppStorage 中的 unionId 属性声明为持久化属性,默认值为空字符串。此后任何对 AppStorage 中 unionId 的修改都会自动写入磁盘,应用重启后也会自动恢复。

咕噜冰箱在多处使用了 PersistentStorage,用于持久化主题模式、提醒时间、VIP 状态等用户偏好数据,与 Preferences 手动管理形成了互补——关键业务数据用手动 Preferences(控制更精细),用户偏好数据用声明式 PersistentStorage(代码更简洁)。

六、状态管理策略总结

机制生命周期适用场景特点
@State组件生命周期页面内局部状态自动触发UI刷新
AppStorage应用生命周期跨页面共享状态全局可读写
Preferences持久化需要跨重启保留的数据手动读写,轻量键值对
PersistentStorage持久化用户偏好自动持久化声明式,自动同步AppStorage

七、踩坑与优化建议

7.1 Preferences 缓存问题

preferences.getPreferencesSync 会返回缓存中的实例。如果在其他地方修改了 Preferences 但未调用 flush,后续读取可能得到旧值。咕噜冰箱在 getPreferences 时主动调用了 removePreferencesFromCacheSync 来清除缓存,确保每次获取的都是最新数据。不过这种方式有性能代价,建议仅在关键路径使用。

7.2 开屏页与冷启动时序

在冷启动场景下,onCreate 中从 Preferences 恢复数据到 AppStorage 是异步的,如果开屏页的 onPageShow 先于数据恢复执行,可能导致误判。解决方案是在 onCreate 中使用同步 API(如 getPreferencesSync + getSync)来确保数据在路由判断前已就绪。

7.3 华为账号登出事件

咕噜冰箱通过订阅 common.event.DISTRIBUTED_ACCOUNT_LOGOUT 系统事件来感知用户在系统设置中登出华为账号的操作,收到事件后立即清除 AppStorage 中的登录信息并跳转到登录页,避免已登出状态下仍可访问应用数据的安全风险。

八、小结

开屏页看似简单,但背后牵涉 HarmonyOS 状态管理体系的多个层次。从内存态的 AppStorage 到持久化的 Preferences 和 PersistentStorage,从路由分发到系统事件监听,每一个环节都需要正确衔接。理解这套机制,不仅是做好开屏页的关键,更是构建整个应用状态管理架构的基础。

Logo

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

更多推荐