公司动态

HarmonyOS社交通讯应用开发 23 : 应用接续原理

📅 2026/8/25 6:21:40
HarmonyOS社交通讯应用开发 23 : 应用接续原理
应用接续原理引言想象这样一个场景你在平板上打开某个应用正在编辑一篇图文并茂的文章写了标题、正文、插入了几张图片和一段视频还选择了发布位置。这时你放下平板拿起手机希望接着刚才的进度继续编辑——正文还在、图片还在、光标位置甚至都可以继续。这就是应用接续Continue要解决的问题让同一个应用的任务在多设备之间无缝迁移。本系列的示例工程 ContinuePublish 演示的正是这一能力设备 A 上编辑内容标题/正文/图片/视频/位置通过 Dock 栏多设备任务中心将任务接续到设备 B数据经分布式数据对象与分布式文件系统跨设备同步。本篇作为模块四的开篇先讲清楚应用接续的整体原理包括工程配置、onContinue生命周期回调的完整实现以及接续数据在发送端是如何被打包出去的。一、什么是应用接续应用接续Application Continuation是 HarmonyOS 提供的跨设备迁移能力。它的本质是当用户在一台设备上通过系统提供的入口如 Dock 栏任务中心、智慧多窗等发起接续操作时系统会唤醒目标设备上同一个应用的同一个 UIAbility 实例让应用把现场数据从源设备搬到目标设备从而实现在目标设备上继续未完成的任务。与分享Share不同接续强调的是同一任务的连续性分享是把数据给出去接续是把任务本身搬过去。与服务流转相比应用接续聚焦于应用内 UIAbility 的迁移是最基础、最常用的一档跨设备能力。应用接续的完整链路可以分为三段发送端打包源设备上 UIAbility 的onContinue(wantParam)回调被系统调用应用在此回调中把需要迁移的数据写入wantParam并返回是否同意接续。系统调度系统把wantParam连同会话标识等额外参数通过分布式能力投递到目标设备并在目标设备上以LaunchReason.CONTINUATION的原因拉起或复用该应用。接收端还原目标设备上应用感知到这是接续启动从want中取出会话标识与发送端建立数据通道把现场数据还原到界面。其中第 1 步由onContinue完成第 3 步由本项目的restoreDistributedObject完成第 2 步由系统完成。本文重点讲第 1 步接收端在第 25 篇《接续数据还原》中详细展开。二、工程配置让应用可以被接续要让一个 UIAbility 支持接续必须先做两件事声明continuable能力并把launchType设为singleton。在entry/src/main/module.json5中abilities:[{name:EntryAbility,srcEntry:./ets/entryability/EntryAbility.ets,exported:true,continuable:true,launchType:singleton}]**continuable: true**告诉系统这个 UIAbility 具备接续能力系统才会在 Dock 栏任务中心对它显示接续到其他设备的入口。如果没有该声明用户根本无法发起接续。**launchType: singleton**指定单实例模式。接续到目标设备时如果目标设备上该应用已经在运行系统不会重新创建一个实例而是把新的want分发给已有的实例此时应用会通过onNewWant收到通知。这保证了接收端只有一份页面状态避免旧实例 新实例同时存在的混乱。此外接续会用到分布式数据同步能力需要申请权限ohos.permission.DISTRIBUTED_DATASYNC。在同一个文件的requestPermissions中声明{name:ohos.permission.DISTRIBUTED_DATASYNC,reason:$string:permission_distributed_datasync,usedScene: {abilities: [EntryAbility],when:inuse} }DISTRIBUTED_DATASYNC属于敏感权限运行时需要弹窗授权。本工程在entry/src/main/ets/view/contentEditor/BottomToolbar.ets中通过atManager.requestPermissionsFromUser请求CommonConstants.REQUEST_PERMISSIONS声明的权限当前列表以定位类权限为主生产工程应将DISTRIBUTED_DATASYNC一并纳入运行时请求清单授权成功后分布式数据对象和分布式文件才能真正跨设备同步。三、接续的触发与前提条件配置就绪后用户是怎么发起接续的在 HarmonyOS 多设备协同的场景中最常见的入口是Dock 栏任务中心长按任务卡片选择接续到其他设备再从弹出的设备列表里选一台目标设备。系统随后执行一次设备协商——这背后有几个隐含前提同一华为账号接续要求两台设备登录同一个华为账号分布式能力以账号为信任边界做设备认证。同一分布式组网两台设备需要处于同一个分布式组网如同 Wi-Fi 局域网、或通过超级终端互联网络质量直接决定接续数据同步的快慢。目标设备安装同应用目标设备上必须已安装 ContinuePublish且该 UIAbility 声明了continuable否则系统会提示无法接续。权限已授权接续过程要跨设备读写数据DISTRIBUTED_DATASYNC若未授权save阶段会失败接续会中断。这些前提由系统与用户共同保证应用层代码只需要在失败时做好日志记录与提示。理解这一点有助于排查接续点了没反应的问题——先检查账号、组网与权限再看代码。四、onContinue发送端的打包现场当用户在某台设备上对 ContinuePublish 发起接续时系统调用发送端EntryAbility的onContinue。这是接续的出口项目实现位于entry/src/main/ets/entryability/EntryAbility.etsasynconContinue(wantParam:Recordstring,Object|undefined):PromiseAbilityConstant.OnContinueResult {// 1. 不迁移页面栈应用自己管理页面跳转wantParam[wantConstant.Params.SUPPORT_CONTINUE_PAGE_STACK_KEY] false;// 2. 媒体列表序列化后随 want 传递供目标设备快速感知wantParam.mediaUriArrayJSON.stringify(AppStorage.getArrayMediaInfo(mediaUriArray));try{// 3. 生成分布式数据对象的会话 IDletsessionId:string distributedDataObject.genSessionId(); wantParam.distributedSessionId sessionId;// 4. 取 Navigation 页面栈栈顶作为接续后要恢复的页面letcurrNavPathNames:string[] AppStorage.getNavPathStack(pageInfos)?.getAllPathName()asstring[]; wantParam.currContinuePageUrl currNavPathNames[currNavPathNames?.length-1];// 5. 把编辑区附件转换为分布式文件资产描述AssetletmediaUriArray AppStorage.getArrayMediaInfo(mediaUriArray);letassets: commonType.Assets [];if(mediaUriArray) {for(leti 0; i mediaUriArray.length; i) {letappend mediaUriArray[i];letattachment: commonType.AssetgetAssetInfo(this.context, append); assets.push(attachment); } }// 6. 组装 ContentInfo 并压平为可同步的对象letcontentInfo:ContentInfonewContentInfo(AppStorage.get(mainTitle),AppStorage.get(textContent),AppStorage.get(mediaUriArray),AppStorage.get(isShowLocalInfo),AppStorage.get(isAddLocalInfo),AppStorage.get(selectLocalInfo), assets );letsource contentInfo.flatAssets();// 7. 创建分布式数据对象并绑定会话this.distributedObject distributedDataObject.create(this.context, source);this.distributedObject.setSessionId(sessionId).catch((err: BusinessError) { hilog.info(DOMAIN,TAG,FORMAT,SetSessionId failed. Cause code:${err.code}, message:${err.message}); });// 8. 把数据保存到目标设备awaitthis.distributedObject.save(wantParam.targetDeviceasstring).catch((err: BusinessError) { hilog.info(DOMAIN,TAG,FORMAT,Failed to save. Code:${err.code}, message:${err.message}); }); }catch(error) { hilog.error(DOMAIN,TAG,FORMAT,distributedDataObject failed,code${(errorasBusinessError).code}); }returnAbilityConstant.OnContinueResult.AGREE; }逐段理解这段代码部分细节将在后文各篇展开第 1~2 步向 wantParam 写入附加参数。wantParam是系统提供给应用的行李舱里面可以放任意可序列化的键值对系统会原样带到目标设备。这里写入两个键SUPPORT_CONTINUE_PAGE_STACK_KEY false明确告诉系统不要帮我迁移页面栈因为本工程的页面栈Navigation 栈由AppStorage中的pageInfos统一管理应用自己知道该恢复哪个页面无需系统默认的栈迁移逻辑介入。mediaUriArray把媒体列表JSON.stringify成字符串放进wantParam。这是轻量备份——正文、图片的描述信息可以通过分布式数据对象异步到达而 want 参数是随着接续启动同步到达的接收端可以先拿到这一份数据兜底。第 3 步生成会话 ID。distributedDataObject.genSessionId()生成一个全局唯一的会话标识。发送端和接收端约定谁拿到这个 ID谁就加入同一个会话组。ID 本身也塞进wantParam接收端在restoreDistributedObject中通过want.parameters?.distributedSessionId取出。第 4 步记录当前页面。从AppStorage取全局的NavPathStackNavigation 页面栈getAllPathName()拿到栈里所有页面名取最后一个栈顶作为接续后应恢复的页面写入wantParam.currContinuePageUrl。例如用户正停在编辑器页面这里就是ContentEditorPage。接收端读到该值后会把它写回AppStorage的continuePageUrl由首页Index检测到后自动replacePath到编辑页详见第 25 篇。第 5~6 步打包附件与正文。遍历AppStorage中的mediaUriArray对每一项调用getAssetInfo生成一个commonType.Asset包含文件名、uri、路径、大小、时间等信息收集成assets数组再把标题、正文、位置开关、位置字符串和assets一起构造成ContentInfo对象最后调用flatAssets()把它压平成适合分布式数据对象同步的键值结构详见第 27 篇。第 7~8 步创建分布式数据对象并保存。distributedDataObject.create(this.context, source)以 source 为初始数据创建一个分布式数据对象setSessionId(sessionId)把它加入会话组save(wantParam.targetDevice)把数据持久化并同步到目标设备。save是异步的await保证数据落库后再返回。关于targetDevice需要多说一句它不是应用自己查出来的而是系统在调用onContinue之前就写入wantParam的预置字段标识用户所选的目标设备。save的参数语义是把这份数据保存并同步到哪台设备传wantParam.targetDevice正好是用户选的设备。如果save失败比如目标设备临时离线代码用.catch记录日志而不抛异常——因为onContinue仍然要返回AGREE让接续流程继续走完数据可以稍后通过会话组机制补传。返回值。一切就绪后返回AbilityConstant.OnContinueResult.AGREE表示我同意接续。如果应用此刻无法接续比如正在播放视频不允许打断可以返回OnContinueResult.REJECT或DISAGREE系统会终止本次接续。四、接收端如何感知这是一次接续目标设备上系统以LaunchReason.CONTINUATION启动应用。项目的onCreate与onNewWant都做了同一件事调用restoreDistributedObject还原数据。onCreate(want: Want,launchParam: AbilityConstant.LaunchParam): void { hilog.info(DOMAIN, TAG, FORMAT, Ability onCreate); this.restoreDistributedObject(want,launchParam);// ... 其余初始化} onNewWant(want: Want,launchParam: AbilityConstant.LaunchParam): void { hilog.info(DOMAIN, TAG, FORMAT, Ability onNewWant); this.restoreDistributedObject(want,launchParam);// ... 其余处理}如果目标设备上应用没有在运行系统创建新实例走onCreate。如果应用已经在运行因为launchType: singleton只会有一个实例系统把新 want 交给现有实例走onNewWant。两个入口都调restoreDistributedObject保证无论哪种情况接续数据都能被还原。而restoreDistributedObject内部的第一步就是校验launchParam.launchReason是否为CONTINUATION只有接续启动才走还原逻辑普通启动直接返回——这个判断让同一个入口函数同时服务普通启动与接续启动两种场景而不互相干扰。此外EntryAbility还实现了onWindowStageRestoreonWindowStageRestore(windowStage:window.WindowStage):void{ hilog.info(DOMAIN, TAG,FORMAT,Ability onWindowStageRestore); this.windowStageInit(windowStage); }在接续场景下系统可能复用旧窗口此时窗口舞台的恢复回调是onWindowStageRestore而不是onWindowStageCreate项目把两者的初始化统一收敛到windowStageInit保证窗口配置沉浸式、安全区、断点等在两种路径下都被正确设置。五、接续生命周期时序把两端生命周期拼在一起一次完整的接续时序是这样的用户在设备 A 的 Dock 栏选择接续到设备 B系统向 A 端 EntryAbility 派发onContinue(wantParam)。A 端在onContinue中打包数据、生成会话 ID、创建并save分布式数据对象返回AGREE。系统把wantParam含distributedSessionId、currContinuePageUrl、mediaUriArray投递给设备 B。设备 B 上系统以LaunchReason.CONTINUATION拉起应用若实例不存在走onCreate若已存在singleton 模式走onNewWant两者都调用restoreDistributedObject。B 端restoreDistributedObject创建空对象、注册status监听、setSessionId入组同时把currContinuePageUrl写入 AppStorage。分布式数据管理完成两端对象合流B 端收到status restored取出数据写入 AppStorage还原媒体附件。首页Index检测到continuePageUrl为编辑器页面replacePath跳转编辑页组件通过StorageLink自动回填内容——接续完成。这个时序里值得注意的节奏是步骤 2 与步骤 4~6 是异步并行的——A 端save落库返回后接续流程即可推进B 端的数据还原不阻塞页面加载页面先起来数据后到。所以接收端绝不能假设页面出现时数据一定在而必须等restored事件。这也解释了为什么项目把取数逻辑全部收敛在status回调里而不是写在onCreate的主流程中。六、容易混淆的概念与常见误区围绕应用接续初学者常有几个混淆点一并厘清接续 ≠ 分享Share。分享是把一份数据文本、图片、链接主动发给另一个应用或设备接收方是消费者接续是把正在进行的任务整体搬到另一台设备接收方是续写者。本工程的wantParam里虽然也塞了mediaUriArray但那只是兜底备份真正的接续是靠分布式数据对象 分布式文件完成的——数据量级与语义完全不同。接续 ≠ 应用流转的全部。HarmonyOS 的跨端能力分几档应用接续本工程、服务流转FA 时代的迁移模型、以及基于协同服务Collaboration Service的更高级协同。应用接续是最基础的UIAbility 迁移理解它之后再学更高阶能力会轻松很多。onContinue不一定在 UI 线程执行也不应做重活。它在 Ability 生命周期内被系统调用打包操作拼字符串、序列化、创建对象、save应当轻量。如果接续前需要大量计算或网络请求应先返回暂不可接续或把重活异步化避免阻塞接续流程。接续数据不是一次性的。本工程把现场数据持久化到了分布式数据库接收端restored后即使设备离线也能还原而且同一会话组的数据会长期存在。这意味着应用不应假设接续是用完即焚——清理逻辑如删除过期会话数据需要应用自己负责。补充wantParam的序列化边界。wantParam作为跨进程、跨设备传递的载体只能承载可序列化的值。onContinue里对mediaUriArray先JSON.stringify再放入正是因为MediaInfo中的PixelMap是内存对象、不能直接进 want——序列化成字符串后才具备传递资格。同理distributedSessionId、currContinuePageUrl都是字符串天然安全。这给我们的启示是凡是进wantParam的数据都要先自问它能不能被序列化。小结应用接续是 HarmonyOS 跨设备能力的基础形态发送端打包现场、系统调度投递、接收端还原现场。工程上一次接续的成功依赖三件事module.json5中声明continuable: true与launchType: singleton并申请DISTRIBUTED_DATASYNC权限发送端在onContinue中把现场数据写进wantParam、生成会话 ID、创建并保存分布式数据对象接收端在onCreate/onNewWant中依据LaunchReason.CONTINUATION识别接续并还原数据。本工程的onContinue还透露了三个关键设计页面栈不迁移、由应用自己通过currContinuePageUrl恢复页面媒体附件走分布式数据对象传描述 分布式文件系统传实体的双通道wantParam同时携带一份轻量备份。接下来的五篇文章将逐一拆解分布式数据对象如何工作第 24 篇、接收端如何还原数据第 25 篇、分布式文件系统如何同步附件第 26 篇、数据模型如何打包第 27 篇、媒体如何重建第 28 篇。