查看: 853|回复: 3

鸿蒙应用接续:onContinue 数据打包与还

[复制链接]
发表于 13 小时前 | 显示全部楼层 |阅读模式
在“轻规划”这类个人规划应用里,用户可能在通勤地铁上用手机编辑五年后的愿景信,回家后希望把书写现场流转到鸿蒙平板,用更大屏幕和实体键盘继续写。HarmonyOS NEXT 提供原生应用级流转底座 Ability Kit(应用接续):两台设备登录同一华为账号、处于近场范围时,平板 Dock 栏会自动浮现手机端“轻规划”流转图标,点击后手机端编辑界面关闭,平板端以相同输入内容甚至光标停留位置打开。下面围绕 EntryAbility 重写 onContinue,拆解源端封包、宿端解析、UI 路由恢复和防闪屏处理。

一、接续管线与职责划分
应用流转由系统底层通过分布式总线在设备间热备数据。源端通过回调把状态写入 Want 数据包,宿端在冷启动或热拉起时解析此包重建界面。核心职责可以拆成四层:源端 EntryAbility.onContinue 负责封包;宿端 EntryAbility.onCreate/onNewWant 负责解包;AppStorage 暂存并传递编辑现场;UI 主入口监听恢复标志并做路由重定向。

二、源端封包:在 onContinue 中写入 wantParams
要让对端还原流转前状态,需要在源端 Ability 的 onContinue 钩子中,把当前业务页面路由、编辑文本草稿和光标偏移量全部存入 wantParams。示例中先从 AppStorage 读取 currentRoute、visionLetterDraft、visionInputCursorOffset,再写入 targetRoute、editingDraft、cursorOffset。正常返回 AGREE 表示同意流转;内部严重错误时返回 REJECT 拦截流转,避免对端拿到空白数据。
  1. import { UIAbility, Want, AbilityConstant } from '@kit.AbilityKit';
  2. export default class EntryAbility extends UIAbility {
  3. // 当应用被流转时,系统会自动触发此回调
  4. onContinue(wantParams: Record<string, Object>): AbilityConstant.OnContinueResult {
  5. try {
  6. // 1. 从 AppStorage 全局临时缓存中读取当前的编辑现场状态
  7. const currentRoute = AppStorage.get<string>('currentRoute') || 'VisionEditPage';
  8. const editingDraft = AppStorage.get<string>('visionLetterDraft') || '';
  9. const selectionOffset = AppStorage.get<number>('visionInputCursorOffset') || 0;
  10. // 2. 将数据塞入流转打包参数中
  11. wantParams['targetRoute'] = currentRoute;
  12. wantParams['editingDraft'] = editingDraft;
  13. wantParams['cursorOffset'] = selectionOffset;
  14. console.info("EntryAbility", "onContinue context saved successfully.");
  15. // 3. 返回 AGREE 表示同意该流转请求
  16. return AbilityConstant.OnContinueResult.AGREE;
  17. } catch (err) {
  18. console.error("EntryAbility", "onContinue save state failed", err);
  19. // 若发生内部严重错误,返回 REJECT 拦截流转,防止对端拿到空白数据
  20. return AbilityConstant.OnContinueResult.REJECT;
  21. }
  22. }
  23. }
复制代码

三、宿端解包:区分冷启动与热启动,判断 CONTINUATION
接收端系统会自动冷启动或热激活“轻规划”。需要在 onCreate(冷启动)或 onNewWant(热启动)中拦截 Want 参数,并判断 launchParam.launchReason 是否为 AbilityConstant.LaunchReason.CONTINUATION。命中后提取 targetRoute、editingDraft、cursorOffset,重新写入宿端 AppStorage,同时设置 isRestoredFromContinuation 为 true,通知 UI 主页面执行路由重定向。
  1. onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  2. this.handleRestoreContinuation(want, launchParam);
  3. }
  4. onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  5. this.handleRestoreContinuation(want, launchParam);
  6. }
  7. private handleRestoreContinuation(want: Want, launchParam: AbilityConstant.LaunchParam) {
  8. // 1. 判断启动原因是否为“流转启动”
  9. if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
  10. console.info("EntryAbility", "Application launched via continuation. Restoring context...");
  11. const wantParams = want.parameters;
  12. if (wantParams) {
  13. // 2. 提取流转上下文
  14. const targetRoute = wantParams['targetRoute'] as string;
  15. const editingDraft = wantParams['editingDraft'] as string;
  16. const cursorOffset = wantParams['cursorOffset'] as number;
  17. // 3. 将还原的数据再次存入宿端本机的 AppStorage
  18. AppStorage.setOrCreate('currentRoute', targetRoute);
  19. AppStorage.setOrCreate('visionLetterDraft', editingDraft);
  20. AppStorage.setOrCreate('visionInputCursorOffset', cursorOffset);
  21. // 4. 触发流转状态全局标识,以便 UI 主页面进行路由强行重定向
  22. AppStorage.setOrCreate('isRestoredFromContinuation', true);
  23. console.info("EntryAbility", `Successfully restored data draft length: ${editingDraft.length}`);
  24. }
  25. }
  26. }
复制代码

四、UI 侧监听:@Watch 加延时挂载规避闪屏
即使 Ability 层把数据还原到 AppStorage,冷启动时前台 UI 组件也可能尚未完全初始化。此时如果路由立即跳转,容易出现路由加载失败或界面闪烁。因此可在 Index.ets 主入口用 @StorageLink 监听 isRestoredFromContinuation,并通过 @Watch 触发 onRestoreTriggered。触发后延时 100ms,等待主窗口容器与路由栈彻底就绪,再根据 currentRoute 跳到 VisionEditPage,最后重置恢复标识,避免重复跳转。
  1. @Entry
  2. @Component
  3. struct MainIndex {
  4. @StorageLink('isRestoredFromContinuation') @Watch('onRestoreTriggered') isRestored: boolean = false;
  5. @StorageLink('currentRoute') currentRoute: string = 'MainTab';
  6. onRestoreTriggered() {
  7. if (this.isRestored) {
  8. // 延时 100ms 等待主窗口容器与路由栈彻底就绪后再发起跳转,规避冷启动瞬间黑屏/闪烁
  9. setTimeout(() => {
  10. if (this.currentRoute === 'VisionEditPage') {
  11. // 发起路由重定向
  12. this.navigateToEditPage();
  13. }
  14. // 重置流转恢复标识
  15. this.isRestored = false;
  16. }, 100);
  17. }
  18. }
  19. }
复制代码

通过这层缓冲保护,接续时平板端可以在极度丝滑且没有中间白屏的体验下直接显示手机端编辑界面。

五、实践检查清单
1. 源端只打包必要现场:页面路由、文本草稿、光标偏移量;异常时用 REJECT 拦截。
2. 宿端冷启动和热启动统一处理:onCreate 与 onNewWant 都调用 handleRestoreContinuation。
3. 用 AppStorage 作为 Ability 到 UI 的桥接,避免 UI 未就绪时丢状态。
4. 用 isRestoredFromContinuation 配合 @Watch 触发一次恢复,完成后重置,防止重复跳转。
5. 100ms 延时是等待窗口容器与路由栈就绪的工程处理,不是业务延时。
6. 流转前提是同一华为账号、近场范围,平板 Dock 栏出现流转图标后用户点击触发。

六、总结
通过重写 EntryAbility 的流转生命周期,“轻规划”可以把手机端编辑现场无缝接续到平板端,打破终端物理界限,形成多端无缝工作流。完成分布式数据库多端同步与跨端接续后,下一步原文预告将进入 Device Security Kit AI 防窥保护,涉及人脸识别敏感视线追踪与毛玻璃屏动态遮蔽。
回复

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: 鸿蒙应用接续:onContinue 数据打包与还

看完很有收获,楼主的思路很清晰。把源端 onContinue 封包、宿端 onCreate 和 onNewWant 解包、AppStorage 暂存、UI 路由恢复这条链路拆开讲,理解起来顺畅很多。onContinue 里从 AppStorage 读当前路由、草稿和光标偏移,再写入 wantParams,同时用 AGREE 和 REJECT 区分正常流转与异常拦截,这个处理很稳,能避免对端拿到空白数据。 宿端把冷启动和热启动统一收口到 handleRestoreContinuation,再根据 launchReason 是否为 CONTINUATION 来决定是否恢复,也很实用。设置 isRestoredFromContinuation 通知 UI 主页面做重定向,这个标志位设计得挺合理。 个人补充两个关注点:一是 wantParams 里尽量放简单、可序列化的数据,复杂对象最好先转换;二是光标恢复最好等文本组件挂载、草稿填充完成后再设置,不然容易出现跳位或闪屏。期待后续把宿端解包和防闪屏部分补全,感谢分享。
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: 鸿蒙应用接续:onContinue 数据打包与还

这个接续方案拆得很清楚,源端 onContinue 封包、宿端 onCreate/onNewWant 解包、AppStorage 暂存、UI 路由重定向,四层职责一眼能看懂。特别认同在 onContinue 里用 try/catch,异常时返回 REJECT,避免对端拿到空白状态。宿端把冷启动和热启动都收敛到 handleRestoreContinuation,再用 launchReason 判断 CONTINUATION,这个统一入口在实际项目里会省很多分支判断。 实际落地时我会比较关注两点:一是 wantParams 里尽量只放路由、光标偏移和短草稿这类轻量可序列化数据,长文本最好另走分布式数据对象或文件,不然容易受包大小和类型限制;二是恢复后设置光标最好等目标输入组件挂载完成,否则容易定位不到输入框。防闪屏可以先在宿端显示统一占位或骨架,等 isRestoredFromContinuation 被 UI 消费完再切编辑页,这样体验会更顺。你最后 AppStorage.setOrCreate 那段好像没贴完,方便的话可以补上宿端完整恢复和防闪屏处理。
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: 鸿蒙应用接续:onContinue 数据打包与还

楼主这篇把 onContinue 封包和宿端解包的链路讲得挺清楚,尤其是源端从 AppStorage 取 currentRoute、visionLetterDraft、visionInputCursorOffset,再写进 wantParams,宿端在 onCreate 和 onNewWant 里统一走 handleRestoreContinuation,并用 launchReason 判断 CONTINUATION,这套职责划分很清晰。源端 catch 里返回 REJECT 防止对端拿到空白数据,这个细节也挺稳。 想请教两个点:一是 wantParams 里直接塞编辑草稿和光标偏移,如果愿景信很长,会不会有大小限制或传输耗时问题,是否更适合只传草稿 ID,然后靠分布式数据同步补内容?二是你写到 AppStorage.setOrCreate('currentRout 这里好像被截断了,后面 UI 主入口监听 isRestoredFromContinuation 做路由重定向和防闪屏的部分没展开。冷启动时页面栈还没准备好,怎么避免先闪一下默认首页再跳到 VisionEditPage?这块如果能补上就太好了。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-10-15 21:57 , Processed in 0.094604 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部