1 引言

应用接续是 HarmonyOS 提供的一项跨设备协同能力:当用户在一台设备上操作某个应用时,可以在另一台设备的相同应用中快速切换,无缝衔接上一个设备的应用体验。在内容编辑、邮件撰写等需要连续性的场景中,用户往往希望从手机切换到平板后,已输入的文字、插入的图片等都能原样保留,而不必重新开始。本文结合两个官方 Codelabs——内容编辑场景与分布式邮件场景——系统介绍如何在 HarmonyOS 应用中适配接续能力,重点说明源端数据保存与对端数据恢复的实现方法,并对关键概念进行讲解。

2 基础概念与前置条件

2.1 应用接续

应用接续,指当用户在一个设备上操作某个应用时,可以在另一个设备的相同应用中快速切换,无缝衔接上一个设备的应用体验。其触发方式通常为:在源端设备前台运行应用,在对端设备 Dock 栏点击该应用的接续图标,系统即启动迁移流程。

2.2 分布式数据对象

分布式数据对象(@ohos.data.distributedDataObject)是 HarmonyOS 提供的一种数据管理能力。它是一个 JS 对象型的封装,每一个分布式数据对象实例会创建一个内存数据库中的数据表;每个应用程序创建的内存数据库相互隔离,对分布式数据对象的“读取”或“赋值”会自动映射到对应数据库的 get/put 操作。在接续场景中,利用分布式数据对象可以在源端设备保存待迁移的数据,并使对端设备加入同一会话后读取该数据,从而实现跨设备数据传递。

2.3 环境与设备要求

硬件要求:两台及以上 HarmonyOS 系统版本为 5.0.5 Release 及以上的设备,支持直板机、双折叠、三折叠、平板等。软件要求:DevEco Studio 6.1.1 Release 及以上,HarmonyOS SDK 6.1.1 Release 及以上。模拟器暂不支持接续功能。

2.4 使用限制与权限

接续功能要求:

  • 双端设备登录同一华为账号;
  • 双端设备打开 WLAN 和蓝牙开关,或在设置中启用“多设备协同增强服务”;
  • 双端设备在设置中开启“多设备协同 > 接续”功能;
  • 双端设备均安装该应用。

权限方面,自 API 12 起无需申请 ohos.permission.DISTRIBUTED_DATASYNC 权限;API 11 及以前版本需要声明该权限,并在首次启动或进入接续页面时向用户申请授权。

3 适配流程总览

应用接续适配可概括为四个步骤:

  1. 启用应用接续能力:在工程配置中声明 UIAbility 可迁移;
  2. 配置应用启动模式:推荐使用单实例模式;
  3. 源端保存迁移数据:在 onContinue 回调中构建并保存待迁移数据;
  4. 对端恢复数据:在生命周期回调中恢复分布式数据对象,并将数据写入 UI。

下面按照该顺序逐一讲解。

4 工程配置:启用接续能力与启动模式

4.1 配置 continuable

module.json5 文件的 abilities 中,将 continuable 标签配置为 true,表示该 UIAbility 可被迁移。默认值为 false,系统将识别为无法迁移。

// entry/src/main/module.json5
{
  "module": {
    "abilities": [
      {
        "continuable": true
      }
    ]
  }
}

4.2 配置 launchType

启动模式影响接续时应用实例的创建策略,推荐使用 singleton(单实例模式)。在内容编辑、邮件编写等接续场景中,单实例可以确保全局只存在一个实例,避免因多实例导致的数据不一致问题。

启动模式 配置值 特点 适用场景
singleton "singleton" 单实例模式,应用全局只存在一个实例 接续场景推荐,确保数据一致性
standard "standard" 多实例模式,每次启动创建新实例 适合需要独立处理多个任务的场景
specified "specified" 指定实例模式,根据 MissionKey 决定是否创建新实例 复杂的文档按 ID 复用实例等场景
{
  "module": {
    "abilities": [
      {
        "launchType": "singleton"
      }
    ]
  }
}

5 源端数据保存与迁移

5.1 数据保存的基本原理

在接续触发前,源端应用需要将用户正在编辑的数据实时保存到全局存储中。HarmonyOS 提供 @StorageLink 装饰器,它可以将组件属性与 AppStorage 全局存储进行双向绑定:当组件中的数据发生变化时,自动更新 AppStorage 中的对应值;反之亦然。这样,在 onContinue 回调触发时,可以从 AppStorage 中读取到最新的编辑内容。

当源端设备满足接续条件且用户点击对端 Dock 栏接续图标时,系统会自动调用源端 UIAbility 的 onContinue 回调。开发者需要在该回调中完成数据打包与传输。需要注意的是,onContinue 回调中通过 wantParam 传输的数据应控制在 100KB 以下;对于大数据量(如图片文件),应使用分布式对象迁移数据。

5.2 构建数据模型

在内容编辑场景中,可以通过静态方法 buildFromAppStorageAppStorage 中构建完整的数据模型。以 ContentInfo 为例,它包含标题、正文、图片及位置信息等字段,形成一个核心数据对象。对于图片等附件资源,需要将 ImageInfo 数组转换为 Asset 数组,并调用 flatAssets() 方法将模型扁平化,转换为可传输的 Record 结构。

5.3 创建并保存分布式数据对象

onContinue 中,核心工作是创建分布式数据对象并保存到目标设备。一般流程如下:

  1. 生成会话 ID:调用 genSessionId() 生成全局唯一标识,用于源端与对端建立组网;
  2. 创建分布式对象:调用 distributedDataObject.create() 创建实例,并填充扁平化后的数据;
  3. 设置会话:调用 setSessionId(sessionId) 加入组网,同时将 sessionId 写入 wantParam.distributedSessionId,以便对端读取后加入同一会话;
  4. 持久化保存:调用 save() 方法将分布式对象保存到目标设备,确保源端应用退出后数据仍可被对端获取。

下面给出一个简化后的 onContinue 实现示例:

// entry/src/main/ets/entryability/EntryAbility.ets
export default class EntryAbility extends UIAbility {
  private distributedObject: distributedDataObject.DataObject | undefined = undefined;

  async onContinue(wantParam: Record<string, Object | undefined>): Promise<AbilityConstant.OnContinueResult> {
    try {
      // 从 AppStorage 构建待迁移的数据模型,并进行扁平化处理
      let content = ContentInfo.buildFromAppStorage();
      let flatData = content.flatAssets();

      // 创建分布式数据对象
      this.distributedObject = distributedDataObject.create(this.context);

      // 将扁平化数据填充到分布式对象中
      for (let key in flatData) {
        this.distributedObject[key] = flatData[key];
      }

      // 生成会话 ID 并设置组网
      let sessionId = distributedDataObject.genSessionId();
      this.distributedObject.setSessionId(sessionId);
      wantParam.distributedSessionId = sessionId;

      // 保存到目标设备
      await this.distributedObject.save(wantParam.targetDevice as string);
    } catch (err) {
      // 记录错误日志
    }

    // 返回同意接续
    return AbilityConstant.OnContinueResult.AGREE;
  }
}

在实际工程中,可将上述步骤封装为一个工具函数(例如 saveDistributedDataForContinue),其执行流程包括生成会话 ID、读取数据、转换资产、构建模型、扁平化处理、创建分布式对象、建立会话、返回结果等,使 onContinue 更简洁。

6 对端数据恢复

当源端完成数据准备并传输后,对端设备需要根据应用当前是否已运行,分别走冷启动或热启动流程执行数据恢复。

6.1 冷启动与热启动

  • 冷启动:应用未运行,系统创建应用实例并调用 onCreate 回调。此时需要判断本次启动是否为接续启动,并初始化分布式数据连接。
  • 热启动:应用已在后台运行,系统不再新建实例,而是调用 onNewWant 回调。此时需要执行接续数据更新逻辑。

无论冷启动还是热启动,在应用迁移启动后,都会在执行完 onCreate/onNewWant 后触发 onWindowStageRestore 生命周期函数,可以在该函数中手动加载要恢复的页面。

为了保证迁移后的应用仍然具备可以迁移回源端的能力,需要在 onCreateonNewWant 中调用 setMissionContinueState(),将迁移状态设置为 ACTIVE

6.2 恢复分布式数据对象

对端恢复流程如下:

  1. 创建空的分布式数据对象,用于接收恢复的数据;
  2. want 参数中读取源端传递过来的分布式数据对象组网 ID(distributedSessionId);
  3. 调用 setSessionId(sessionId) 加入与源端一致的组网,激活分布式数据对象;
  4. 注册状态监听:调用 on() 接口监听数据变更,当收到 statusrestored 的事件回调时,表示数据已恢复完成;
  5. 在回调中通过分布式数据对象读取源端保存的数据,并写入 AppStorage,供页面使用。

以下是对端恢复的示例代码(简化):

// entry/src/main/ets/entryability/EntryAbility.ets
export default class EntryAbility extends UIAbility {
  private distributedObject: distributedDataObject.DataObject | undefined = undefined;

  async restoreDistributedObject(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
    // 设置迁移状态为 ACTIVE,保证后续可继续迁移
    this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE);

    // 创建空的分布式数据对象
    this.distributedObject = distributedDataObject.create(this.context);

    // 从 want 读取组网 ID 并加入会话
    let sessionId = want.parameters?.distributedSessionId as string;
    this.distributedObject.setSessionId(sessionId);

    // 注册状态监听
    this.distributedObject.on('status', (data) => {
      if (data.status === 'restored') {
        // 将分布式对象中的数据恢复到 AppStorage
        AppStorage.setOrCreate('mainTitle', this.distributedObject['mainTitle']);
        AppStorage.setOrCreate('textContent', this.distributedObject['textContent']);
        // 如有文件资产,执行文件复制
        // ...
      }
    });

    // 恢复窗口舞台
    this.context.restoreWindowStage(new LocalStorage());
  }

  onWindowStageRestore(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/Home', (err, data) => {
      if (err.code) {
        // 日志
        return;
      }
    });
  }
}

6.3 数据恢复到 AppStorage

文本类数据(如标题、正文、邮件收件人、发件人、主题等)可以直接从分布式数据对象中读取并写入 AppStorage。页面组件通过 @StorageLink 绑定这些键值,当 AppStorage 数据变化时,UI 会自动刷新,无需手动调用刷新方法。

6.4 文件资产迁移与复制

在内容编辑场景中,图片等文件资产通过分布式对象进行传输。数据恢复时,需要将这些文件从分布式目录复制到本地目录,以便应用正常访问。一般流程为:

  • 遍历分布式对象中的附件资产;
  • 将文件从 distributedFilesDir 复制到本地 filesDir 目录;
  • 读取文件并创建 PixelMap 图像对象,供页面展示。

这一过程通常封装在 fileCopy() 之类的工具函数中,包含文件读取、写入、图像源创建等完整流程,确保接续后图片能够正常显示。

6.5 页面数据自动刷新

页面组件使用 @StorageLink 装饰器绑定 AppStorage 中的数据,实现了双向数据绑定。具体机制为:当 AppStorage 中数据变化时,系统自动触发组件监测,执行页面重新渲染。例如:

@Component
export struct EditorComponent {
  @StorageLink('mainTitle') mainTitle: string = '';
  @StorageLink('textContent') textContent: string = '';

  build() {
    Flex({ direction: FlexDirection.Column }) {
      TextInput({ text: this.mainTitle, placeholder: '请输入标题' })
        .onChange((value: string) => {
          this.mainTitle = value;
        })
      TextArea({ text: this.textContent, placeholder: '请输入正文' })
        // ...
    }
  }
}

上述代码中,mainTitletextContent 通过 @StorageLink 与全局存储绑定;源端编辑时实时更新 AppStorage,对端恢复后 AppStorage 更新,页面自动同步显示。

7 分布式邮件场景的页面实现要点

在“分布式邮件”示例中,页面主要由顶部标题栏和邮件信息内容区两部分组成。顶部标题栏使用 Flex 容器组件以弹性方式布局子组件,通过 SymbolGlyph 展示返回按钮和发送按钮,使用 Text 组件展示应用标题,并通过 flexGrow 属性使标题占满剩余空间。邮件信息内容区使用 Row 组件逐行展示收件人、发件人、主题和内容,使用 TextInputTextArea 组件展示具体信息并实现输入,数据来源为 AppStorage 中存储的邮件信息。

8 注意事项与最佳实践

  1. 大数据量应使用分布式对象onContinue 回调中通过 wantParam 传输的数据需控制在 100KB 以下,图片、文件等资产应通过分布式数据对象迁移。
  2. 合理选择启动模式:接续场景推荐 singleton 单实例模式,确保数据一致性;如需多任务并行,可考虑 standardspecified
  3. 会话 ID 一致性:源端通过 genSessionId() 生成的会话 ID 必须通过 wantParam.distributedSessionId 传递给对端,对端使用相同 ID 调用 setSessionId() 才能加入同一组网。
  4. 恢复后保持可迁移能力:对端在 onCreate/onNewWant 中调用 setMissionContinueState() 将状态设置为 ACTIVE,保证迁移后的应用仍可迁回源端。
  5. 监听恢复状态:对端应在分布式对象上注册状态监听,待收到 restored 事件后再进行数据读取和 UI 更新,避免数据尚未同步完成就读取导致内容缺失。
  6. 文件资产路径处理:对端恢复文件时,要将文件从 distributedFilesDir 复制到本地 filesDir,并创建相应图像对象,确保文件可长期访问。
  7. 满足双端条件:开发调试前确保双端登录同一华为账号、开启 WLAN 和蓝牙或“多设备协同增强服务”、开启“接续”功能、双端均安装应用。

9 总结

本文基于内容编辑与分布式邮件两个场景,详细介绍了 HarmonyOS 应用接续功能的适配方法。核心步骤包括:在 module.json5 中启用 continuable 并配置合适的启动模式;在源端通过 @StorageLink 实时保存数据,并在 onContinue 回调中使用分布式数据对象保存和传输数据;在对端根据冷启动或热启动分别处理,恢复分布式对象、监听恢复状态、将数据写入 AppStorage,并完成文件资产复制与页面自动刷新。掌握这些方法后,开发者可以高效地为应用添加跨设备无缝接续能力,提升用户的连续工作体验。

Logo

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

更多推荐