当前位置: 首页 > news >正文

HarmonyOS NEXT:多设备的自由流转

自由流转概述

场景介绍

随着全场景多设备的生活方式不断深入,用户拥有的设备越来越多,不同设备都能在适合的场景下提供良好的体验,例如手表可以提供及时的信息查看能力,电视可以带来沉浸的观影体验。但是,每个设备也有使用场景的局限,例如在电视上输入文本相对移动设备来说是非常糟糕的体验。当多个设备通过分布式操作系统能够相互感知、进而整合成一个超级终端时,设备与设备之间就可以取长补短、相互帮助,为用户提供更加自然流畅的分布式体验。

在HarmonyOS中,将跨多设备的分布式操作统称为流转;根据使用场景的不同,流转又分为跨端迁移和多端协同两种具体场景。

基本概念

  • 流转

    在HarmonyOS中泛指跨多设备的分布式操作。流转能力打破设备界限,多设备联动,使用户应用程序可分可合、可流转,实现如邮件跨设备编辑、多设备协同健身、多屏游戏等分布式业务。流转提供更广的使用场景和更新的产品视角,强化产品优势,实现体验升级。流转按照使用场景可分为跨端迁移多端协同

  • 跨端迁移

    在用户使用设备的过程中,当使用情境发生变化时(例如从室内走到户外或者周围有更合适的设备等),之前使用的设备可能已经不适合继续当前的任务,此时,用户可以选择新的设备来继续当前的任务,原设备可按需决定是否退出任务,这就是跨端迁移场景。

    常见的跨端迁移场景实例:在平板上播放的视频,迁移到智慧屏继续播放,从而获得更佳的观看体验;平板上的视频应用退出。

    在应用开发层面,跨端迁移指在A端运行的UIAbility迁移到B端上,完成迁移后,B端UIAbility继续任务,而A端UIAbility可按需决定是否退出。

  • 多端协同

    用户拥有的多个设备,可以作为一个整体,为用户提供比单设备更加高效、沉浸的体验,这就是多端协同场景。

    常见的多端协同场景实例:

    场景一:两台设备A和B打开备忘录同一篇笔记进行双端协同编辑,在设备A上可以使用本地图库中的图片资源插入编辑,设备B上进行文字内容编辑。

    场景二:设备A上正在和客户进行聊天,客户需要的资料在设备B上,可以通过聊天软件打开设备B上的文档应用选择到想要的资料回传到设备A上,然后通过聊天软件发送给客户。

    在应用开发层面,多端协同指多端上的不同UIAbility/ServiceExtensionAbility同时运行、或者交替运行实现完整的业务;或者多端上的相同UIAbility/ServiceExtensionAbility同时运行实现完整的业务。

注意

  • 当前不支持三方应用实现ServiceExtensionAbility。如果想要实现后台处理相关事务的功能,可以使用后台任务,具体请参见后台任务。
  • 三方应用的UIAbility组件可以通过Context连接系统提供的ServiceExtensionAbility。
  • 三方应用需要在前台获焦的情况下才能连接系统提供的ServiceExtensionAbility。

典型场景

  • 媒体播控:使用媒体播控,可以简单高效地将音频投放到其他HarmonyOS设备上播放,如在手机上播放的音频,可以投到2in1设备上继续播放。
  • 应用接续:指当用户在一个设备上操作某个应用时,可以在另一个设备的同一个应用中快速切换,并无缝衔接上一个设备的应用体验。
  • 跨设备拖拽:跨端拖拽提供跨设备的键鼠共享能力,支持在平板或2in1类型的任意两台设备之间拖拽文件、文本。
  • 跨设备剪贴板:当用户拥有多台设备时,可以通过跨设备剪贴板的功能,在A设备的应用上复制一段文本,粘贴到B设备的应用中,高效地完成多设备间的内容共享。

自由流转的两种形态

应用接续 

应用接续特性简介 

应用接续,指当用户在一个设备上操作某个应用时,可以在另一个设备的同一个应用中快速切换,并无缝衔接上一个设备的应用体验。

比如在用户使用过程中,使用情景发生了变化,之前使用的设备不再适合继续当前任务,或者周围有更合适的设备,此时用户可以选择使用新的设备来继续当前的任务。接续完成后,之前设备的应用可退出或保留,用户可以将注意力集中在被拉起的设备上,继续执行任务。

如图所示,在手机上编辑备忘录,到办公室后切换到平板上继续编辑,完成任务的无缝衔接。

运作机制

  1. 在源端,通过UIAbility的onContinue()回调,可以保存待接续的业务数据。

    例如,浏览器应用完成应用接续,在源端浏览一个页面,到对端继续浏览。系统将自动保存页面状态,如当前页面的浏览进度;需要通过onContinue接口保存页面url等业务内容。

  2. 分布式框架提供跨设备应用界面、页面栈以及业务数据的保存和恢复机制,负责将数据从源端发送到对端。
  3. 在对端,同一UIAbility通过onCreate/onNewWant接口恢复业务数据。

发起接续的场景

针对不同类型的应用,推荐的应用接续发起的界面及接续同步内容如下:

  • 浏览器:网页内容详情页,网页浏览进度同步
  • 备忘录:备忘录详情页,备忘浏览进度同步
  • 新闻:新闻详情页,新闻浏览进度同步
  • 阅读:小说阅读页,小说阅读进度同步
  • 视频:视频播放页,视频播放进度同步
  • 音乐:音乐播放页,歌单播放页,音乐播放进度同步
  • 会议:会议界面,当前会议同步
  • 邮件:新建邮件、回复转发邮件、阅读某封邮件界面,编辑内容及附件同步
  • 办公编辑:某条编辑页面,编辑内容同步
  • CAD:CAD编辑界面,编辑内容同步
  • 地图:路线查询、导航界面,当前路线及导航同步

应用接续开发指导

通过应用接续,可以实现将应用当前任务(包括页面控件状态变量等)迁移到目标设备,并在目标设备上接续使用。

可以实现的功能包括:

  • 存储及恢复自定义数据(应用业务内容)。
  • 存储及恢复页面路由信息和页面控件状态数据。
  • 应用兼容性检测。
  • 支持应用根据实际使用场景动态设置迁移状态(默认迁移状态为ACTIVE激活状态)。

    如编辑类应用在编辑文本的页面下才需要迁移,其他页面不需要迁移,则可以通过setMissionContinueState进行控制。

  • 支持应用动态选择是否进行页面栈恢复(默认进行页面栈信息恢复)。

    如应用希望自定义迁移到其他设备后显示的页面,则可以通过wantConstant.Params进行控制。

  • 支持应用动态选择流转成功后是否退出迁移源端应用(默认流转成功后退出迁移源端应用)。则可以通过@ohos.app.ability.wantConstant (wantConstant)进行控制。

约束与限制

需同时满足以下条件,才能使用该功能:

  • 设备限制

    HarmonyOS NEXT Developer Preview0及以上版本的设备。

  • 使用限制
    • 双端设备需要登录同一华为账号。
    • 双端设备需要打开Wi-Fi和蓝牙开关。

      条件允许时,建议双端设备接入同一个局域网,可提升数据传输的速度。

    • 应用接续只能在同应用(UIAbility)之间触发,双端设备都需要有该应用。
    • 为了接续体验,在onContinue回调中使用wantParam传输的数据需要控制在100KB以下,大数据量请使用分布式数据对象进行同步。

接口说明

以下为实现应用接续的主要接口,详细的接口说明可查阅参考文档。

接口名

描述

onContinue(wantParam : {[key: string]: Object}): OnContinueResult

接续源端在该回调中保存迁移所需要的数据,同时返回是否同意迁移:

  • AGREE:表示同意。
  • REJECT:表示拒绝,如应用在onContinue中异常可以直接REJECT。
  • MISMATCH:表示版本不匹配,接续源端应用可以在onContinue中获取到迁移对端应用的版本号,进行协商后,如果版本不匹配导致无法迁移,可以返回该错误。

onCreate(want: Want, param: AbilityConstant.LaunchParam): void;

接续目的端为冷启动或多实例应用热启动时,在该回调中完成数据恢复,并触发页面恢复。

onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void;

接续目的端为单实例应用热启动时,在该回调中完成数据恢复,并触发页面恢复。

开发步骤

  1. 启用应用接续能力。

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

    {"module": {..."abilities": [{..."continuable": true,}]}
    }

  2. 根据需要配置应用启动模式类型,配置详情请参照UIAbility组件启动模式。
  3. 在源端UIAbility中实现onContinue()接口。

    3.1:当应用触发迁移时,onContinue()接口在源端被调用,可以在该接口中保存迁移数据,实现应用兼容性检测,决定是否支持此次迁移。
    3.2:保存迁移数据:可以将要迁移的数据通过键值对的方式保存在wantParam中。
    (可选)检测应用兼容性:可以在触发迁移时从onContinue()入参wantParam.version获取到迁移对端应用的版本号,与迁移源端应用版本号做兼容校验。应用在校验版本兼容性失败后,需要提示用户迁移失败的原因。

    说明
    如果迁移过程中的兼容性问题对于应用迁移体验影响较小或无影响,可以跳过该步骤。

    3.3:返回迁移结果:可以通过onContinue()回调的返回值决定是否支持此次迁移,接口返回值详见AbilityConstant.OnContinueResult。

    onContinue()接口传入的wantParam参数中,有部分字段由系统预置,可以使用这些字段用于业务处理。同时,应用在保存自己的wantParam参数时,也应注意不要使用同样的key值,避免被系统覆盖导致数据获取异常。详见下表:

    字段

    含义

    version

    对端应用的版本号

    targetDevice

    对端设备的networkId

    import { AbilityConstant, UIAbility } from '@kit.AbilityKit';
    import { hilog } from '@kit.PerformanceAnalysisKit';
    import { promptAction } from '@kit.ArkUI';export default class EntryAbility extends UIAbility {onContinue(wantParam: Record<string, Object>) {let targetVersion  = wantParam.version;  // 获取迁移对端应用的版本号// 应用可根据源端版本号设置支持接续的最小兼容版本号,源端版本号可从app.json5文件中的versionCode字段获取;防止目标端版本号过低导致不兼容。let versionThreshold: number = 0; // 替换为应用自己支持兼容的最小版本号// 兼容性校验    if (targetVersion < versionThreshold) {// 建议在校验版本兼容性失败后,提示用户拒绝迁移的原因promptAction.showToast({message: '目标端应用版本号过低,不支持接续,请您升级应用版本后再试',duration: 2000})// 在兼容性校验不通过时返回MISMATCHreturn AbilityConstant.OnContinueResult.MISMATCH;}console.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`)// 迁移数据保存let continueInput = '迁移的数据';if (continueInput) {// 将要迁移的数据保存在wantParam的自定义字段(如:data)中;wantParam["data"] = continueInput;}return AbilityConstant.OnContinueResult.AGREE;}
    }

  4. 在Stage模型中,应用在不同启动模式下将调用不同的接口,以恢复数据、加载界面。

    不同情况下的函数调用如下图所示:

    说明
    1、在应用迁移启动时,无论是冷启动还是热启动,都会在执行完onCreate()/onNewWant()后,触发onWindowStageRestore()生命周期函数,不执行onWindowStageCreate()生命周期函数。
    2、如果在onWindowStageCreate()中进行了一些应用启动时必要的初始化,那么迁移后需要在onWindowStageRestore()中执行同样的初始化操作,避免应用异常。

    在目的端设UIAbility中实现onCreate()与onNewWant()接口,恢复迁移数据。
    1. 目的端设备上,在onCreate中根据launchReason判断该次启动是否为迁移LaunchReason.CONTINUATION。

    2. 可以从want中获取保存的迁移数据。

    3. 若使用系统页面栈恢复功能,则需要在onCreate()/onNewWant()执行完成前,调用restoreWindowStage(),来触发带有页面栈的页面恢复,如果不需要迁移页面栈可以参考按需迁移页面栈部分。
       

      import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';export default class EntryAbility extends UIAbility {storage : LocalStorage = new LocalStorage();onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {// 将上述的保存的数据取出恢复let continueInput = '';if (want.parameters != undefined) {continueInput = JSON.stringify(want.parameters.data);console.info(`continue input ${continueInput}`)}// 触发页面恢复this.context.restoreWindowStage(this.storage);}}
      }

    4. 如果是单实例应用,需要额外实现onNewWant()接口,实现方式与onCreate()的实现相同。

      在onNewWant()中判断迁移场景,恢复数据,并触发页面恢复
       

      import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';export default class EntryAbility extends UIAbility {storage : LocalStorage = new LocalStorage();onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {console.info(`EntryAbility onNewWant ${AbilityConstant.LaunchReason.CONTINUATION}`)if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {// 将上述的保存的数据取出恢复let continueInput = '';if (want.parameters != undefined) {continueInput = JSON.stringify(want.parameters.data);console.info(`continue input ${continueInput}`);}this.context.restoreWindowStage(this.storage);}}
      }

迁移功能可选配置

动态配置迁移能力

从API version 10起,提供了支持动态配置迁移能力的功能。即应用可以根据实际使用场景,在需要迁移功能时,设置开启应用迁移能力;在业务不需要迁移时,则可以关闭迁移能力。可以通过调用setMissionContinueState接口对迁移能力进行设置。

接口状态值

含义

AbilityConstant.ContinueState.ACTIVE

应用当前可迁移能力开启

AbilityConstant.ContinueState.INACTIVE

应用当前可迁移能力关闭

设置迁移能力的时机

默认状态下,应用的迁移能力为ACTIVE状态,即可以迁移。

如果需要实现某些特殊场景,比如只在具体某个页面下支持迁移,或者只在某个事件发生时才支持迁移,可以按照如下步骤进行配置。

  1. 在Ability的onCreate生命周期回调中,关闭迁移能力。
    // EntryAbility.ets
    import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
    export default class EntryAbility extends UIAbility {onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {// ...this.context.setMissionContinueState(AbilityConstant.ContinueState.INACTIVE, (result) => {console.info(`setMissionContinueState: ${JSON.stringify(result)}`);});// ...}
    }

  2. 如果需要在具体某个页面中打开迁移能力,可以在该页面的onPageShow()函数中设置。
    // PageName.ets
    import { AbilityConstant, common } from '@kit.AbilityKit';
    @Entry
    @Component
    struct PageName {private context = getContext(this) as common.UIAbilityContext;build() {// ...}// ...onPageShow(){// 进入该页面时,将应用设置为可迁移状态this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {console.info('setMissionContinueState ACTIVE result: ', JSON.stringify(result));});}
    }

  3. 如果在某个组件的触发事件中打开迁移能力,可以在该事件中设置。下面以Button组件的onClick事件为例进行介绍。
    // PageName.ets
    import { AbilityConstant, common } from '@kit.AbilityKit';
    @Entry
    @Component
    struct PageName {private context = getContext(this) as common.UIAbilityContext;build() {// ...Button() {//...}.onClick(()=>{//点击该按钮时,将应用设置为可迁移状态this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {console.info('setMissionContinueState ACTIVE result: ', JSON.stringify(result));});})}
    }

保证迁移连续性 

由于迁移加载时,目标端拉起的应用可能执行过自己的迁移状态设置命令(如:冷启动时目标端在onCreate中设置了INACTIVE;热启动时对端已打开了不可迁移的页面,迁移状态为INACTIVE等情况)。为了保证迁移过后的应用依然具有可以迁移回源端的能力,应在onCreate和onNewWant的迁移调用判断中,将迁移状态设置为ACTIVE。 

// EntryAbility.ets
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
export default class EntryAbility extends UIAbility {// ...onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {// ...// 迁移冷启动时,设置状态为可迁移this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {console.info(`setMissionContinueState: ${JSON.stringify(result)}`);});}onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {// ...// 迁移热启动时,设置状态为可迁移if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {console.info('setMissionContinueState ACTIVE result: ', JSON.stringify(result));});}}// ...
}
按需迁移页面栈

支持应用动态选择是否进行页面栈恢复(默认进行页面栈信息恢复)。如果应用不想使用系统默认恢复的页面栈,则可以设置不进行页面栈迁移,而需要在onWindowStageRestore设置迁移后进入的页面,参数定义见SUPPORT_CONTINUE_PAGE_STACK_KEY。

 说明

  1. 当前仅支持router路由的页面栈信息自动恢复,暂不支持navigation路由的页面栈自动恢复。
  2. 如果应用使用navigation路由,可以设置不进行页面栈迁移,并将需要接续的页面(或页面栈)信息保存在want中传递,然后在目标端手动加载指定页面。

应用在源端的页面栈中存在Index和Second路由,而在目标端恢复时不需要按照源端页面栈进行恢复,需要恢复到指定页面。

示例:应用迁移不需要自动迁移页面栈信息

// EntryAbility.ets
import { AbilityConstant, UIAbility, wantConstant } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class EntryAbility extends UIAbility {// ...onContinue(wantParam: Record<string, Object>) {console.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`);wantParam[wantConstant.Params.SUPPORT_CONTINUE_PAGE_STACK_KEY] = false;return AbilityConstant.OnContinueResult.AGREE;}// ...onWindowStageRestore(windowStage: window.WindowStage) {// 若不需要自动迁移页面栈信息,则需要在此处设置应用迁移后进入的页面windowStage.loadContent('pages/Index', (err, data) => {if (err.code) {console.info('Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');return;}console.info('Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');});}
}

按需退出

支持应用动态选择迁移成功后是否退出迁移源端应用(默认迁移成功后退出迁移源端应用)。如果应用不想让系统自动退出迁移源端应用,则可以设置不退出,参数定义见SUPPORT_CONTINUE_SOURCE_EXIT_KEY。

示例:应用迁移设置不需要迁移成功后退出迁移源端应用

import { AbilityConstant, UIAbility, wantConstant } from '@kit.AbilityKit';
export default class EntryAbility extends UIAbility {// ...onContinue(wantParam: Record<string, Object>) {console.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`);wantParam[wantConstant.Params.SUPPORT_CONTINUE_SOURCE_EXIT_KEY] = false;return AbilityConstant.OnContinueResult.AGREE;}// ...
}
支持同应用中不同Ability跨端迁移

一般情况下,跨端迁移的双端是同Ability之间,但有些应用在不同设备类型下的同一个业务Ability名称不同(即异Ability),为了支持该场景下的两个Ability之间能够完成迁移,可以通过在module.json5文件的abilities标签中配置迁移类型continueType进行关联。 需要迁移的两个Ability的continueType字段取值必须保持一致,示例如下:

说明

  • continueType在本应用中要保证唯一,字符串以字母、数字和下划线组成,最大长度127个字节,不支持中文。
  • continueType标签类型为字符串数组,如果配置了多个字段,当前仅第一个字段会生效。
 // 设备A{"module": {// ..."abilities": [{// ..."name": "Ability-deviceA","continueType": ['continueType1'], // continueType标签配置}]}}// 设备B{"module": {// ..."abilities": [{// ..."name": "Ability-deviceB","continueType": ['continueType1'], // 与设备A相同的continueType标签}]}}

快速启动目标应用

默认情况下,发起迁移后不会立即拉起对端的目标应用,而是等待迁移数据从源端传输到对端后才会拉起应用。若应用希望在用户发起接续后立即被拉起,减少等待时间,提升体验,可以在module.json5文件的continueType标签中添加“_ContinueQuickStart”后缀,配置快速启动目标应用能力。示例如下:

{"module": {// ..."abilities": [{// ..."name": "EntryAbility""continueType": ['EntryAbility_ContinueQuickStart'], // 如果已经配置了continueType标签,可以在该标签值后添加'_ContinueQuickStart'后缀;如果没有配置continueType标签,可以使用AbilityName + '_ContinueQuickStart'作为continueType标签实现快速拉起目标应用}]}
}

配置了快速拉起的应用,在用户发起接续时会立即收到一次launchReason为提前拉起(PREPARE_CONTINUATION)的onCreate()/onNewWant()请求,随后再收到一次launchReason为接续拉起(CONTINUATION)的onNewWant()请求。如下所示:

场景

生命周期函数

launchParam.launchReason

第一次启动请求

onCreate (冷启动)

或 onNewWant (热启动)

AbilityConstant.LaunchReason.PREPARE_CONTINUATION

第二次启动请求

onNewWant

AbilityConstant.LaunchReason.CONTINUATION

如果没有配置快速拉起,则触发迁移时只会收到一次启动请求:

场景

生命周期函数

launchParam.launchReason

一次启动请求

onCreate (冷启动)

或 onNewWant (热启动)

AbilityConstant.LaunchReason.CONTINUATION

配置快速拉起后,对应的 onCreate()/onNewWant() 接口实现可参考如下示例:

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';const TAG: string = '[MigrationAbility]';
const DOMAIN_NUMBER: number = 0xFF00;export default class MigrationAbility extends UIAbility {storage : LocalStorage = new LocalStorage();onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onCreate');// 1.已配置快速拉起功能,应用立即启动时触发应用生命周期回调if (launchParam.launchReason === AbilityConstant.LaunchReason.PREPARE_CONTINUATION) {//若应用迁移数据较大,可在此处添加加载页面(页面中显示loading等)//可处理应用自定义跳转、时序等问题// ...}}onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {hilog.info(DOMAIN_NUMBER, TAG, 'onNewWant');// 1.已配置快速拉起功能,应用立即启动时触发应用生命周期回调if (launchParam.launchReason === AbilityConstant.LaunchReason.PREPARE_CONTINUATION) {//若应用迁移数据较大,可在此处添加加载页面(页面中显示loading等)//可处理应用自定义跳转、时序等问题// ...}// 2.迁移数据恢复时触发应用生命周期回调if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {// 将上述保存的数据从want.parameters中取出恢复let continueInput = '';if (want.parameters !== undefined) {continueInput = JSON.stringify(want.parameters.data);hilog.info(DOMAIN_NUMBER, TAG, `continue input ${JSON.stringify(continueInput)}`);}// 触发页面恢复this.context.restoreWindowStage(this.storage);}}
}

使用分布式数据对象迁移数据

当需要迁移的数据较大(100KB以上)或需要迁移文件时,可以使用分布式数据对象。原理与接口说明详见分布式数据对象跨设备数据同步。 

说明

自API 12起,由于直接使用跨设备文件访问实现文件的迁移,难以获取文件同步完成的时间。为了保证更高的成功率,文件的迁移不建议继续通过该方式实现,推荐使用分布式数据对象携带资产的方式。此前通过跨设备文件访问实现的文件迁移依然生效。

 申请权限
  • 自API 12起,无需申请ohos.permission.DISTRIBUTED_DATASYNC权限。
  • API 11及以前版本,需要执行如下操作。
  1. 声明ohos.permission.DISTRIBUTED_DATASYNC权限,详见声明权限。
  2. 由于ohos.permission.DISTRIBUTED_DATASYNC权限需要用户授权,应用需在首次启动、或进入接续页面时弹窗向用户申请授权,详见向用户申请授权。
基础数据迁移

使用分布式数据对象,与上述开发步骤类似,需要在源端onContinue()接口中进行数据保存,并在对端的onCreate()/onNewWant()接口中进行数据恢复。

  1. 在源端UIAbility的onContinue()接口中创建分布式数据对象并保存数据,执行流程如下:
    1.1:在onContinue()接口中使用create()接口创建分布式数据对象,将所要迁移的数据填充到分布式数据对象数据中。
    1.2:调用genSessionId()接口生成数据对象组网id,并使用该id调用setSessionId()加入组网,激活分布式数据对象。
    1.3:使用save()接口将已激活的分布式数据对象持久化,确保源端退出后对端依然可以获取到数据。
    1.4:将生成的sessionId通过want传递到对端,供对端激活同步使用。
     
    import { distributedDataObject } from '@kit.ArkData';
    import { UIAbility, AbilityConstant } from '@kit.AbilityKit';
    import { BusinessError } from '@kit.BasicServicesKit';
    import { hilog } from '@kit.PerformanceAnalysisKit';
    const TAG: string = '[MigrationAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;// 业务数据定义
    class ParentObject {mother: stringfather: stringconstructor(mother: string, father: string) {this.mother = motherthis.father = father}
    }// 支持字符、数字、布尔值、对象的传递
    class SourceObject {name: string | undefinedage: number | undefinedisVis: boolean | undefinedparent: ParentObject | undefinedconstructor(name: string | undefined, age: number | undefined, isVis: boolean | undefined, parent: ParentObject | undefined) {this.name = namethis.age = agethis.isVis = isVisthis.parent = parent}
    }export default class MigrationAbility extends UIAbility {d_object?: distributedDataObject.DataObject;async onContinue(wantParam: Record<string, Object>): Promise<AbilityConstant.OnContinueResult> {// ...let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad');let source: SourceObject = new SourceObject("jack", 18, false, parentSource);// 创建分布式数据对象this.d_object = distributedDataObject.create(this.context, source);// 生成数据对象组网id,激活分布式数据对象let dataSessionId: string = distributedDataObject.genSessionId();this.d_object.setSessionId(dataSessionId);// 将组网id存在want中传递到对端wantParam['dataSessionId'] = dataSessionId;// 数据对象持久化,确保迁移后即使应用退出,对端依然能够恢复数据对象// 从wantParam.targetDevice中获取到对端设备的networkId作为入参await this.d_object.save(wantParam.targetDevice as string).then((result:distributedDataObject.SaveSuccessResponse) => {hilog.info(DOMAIN_NUMBER, TAG, `Succeeded in saving. SessionId: ${result.sessionId},version:${result.version}, deviceId:${result.deviceId}`);}).catch((err: BusinessError) => {hilog.error(DOMAIN_NUMBER, TAG, 'Failed to save. Error: ', JSON.stringify(err) ?? '');});}
    }

  2. 在对端UIAbility的onCreate()/onNewWant()中,通过加入与源端一致的分布式数据对象组网进行数据恢复
    2.1:创建空的分布式数据对象,用于接收恢复的数据;
    2.2:从want中读取分布式数据对象组网id;
    2.3:注册on()接口监听数据变更。在收到status为restore的事件的回调中,实现数据恢复完毕时需要进行的业务操作。
    2.4:调用setSessionId()加入组网,激活分布式数据对象。
    示例代码如下

     

    import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
    import { distributedDataObject } from '@kit.ArkData';
    import { hilog } from '@kit.PerformanceAnalysisKit';
    const TAG: string = '[MigrationAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;// 示例数据对象定义与上同export default class MigrationAbility extends UIAbility {d_object?: distributedDataObject.DataObject;onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {// ...// 调用封装好的分布式数据对象处理函数this.handleDistributedData(want);}}onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {if (want.parameters !== undefined) {// ...// 调用封装好的分布式数据对象处理函数this.handleDistributedData(want);}}}handleDistributedData(want: Want) {// 创建空的分布式数据对象let remoteSource: SourceObject = new SourceObject(undefined, undefined, undefined, undefined);this.d_object = distributedDataObject.create(this.context, remoteSource);// 读取分布式数据对象组网idlet dataSessionId = '';if (want.parameters !== undefined) {dataSessionId = want.parameters.dataSessionId as string;}// 添加数据变更监听this.d_object.on("status", (sessionId: string, networkId: string, status: 'online' | 'offline' | 'restored') => {hilog.info(DOMAIN_NUMBER, TAG, "status changed " + sessionId + " " + status + " " + networkId);if (status == 'restored') {if (this.d_object) {// 收到迁移恢复的状态时,可以从分布式数据对象中读取数据hilog.info(DOMAIN_NUMBER, TAG, "restored name:" + this.d_object['name']);hilog.info(DOMAIN_NUMBER, TAG, "restored parents:" + JSON.stringify(this.d_object['parent']));}}});// 激活分布式数据对象this.d_object.setSessionId(dataSessionId);}
    }

文件资产迁移

对于图片、文档等文件类数据,需要先将其转换为资产commonType.Asset类型,再封装到分布式数据对象中进行迁移。迁移实现方式与普通的分布式数据对象类似,下面仅针对差异部分进行说明。

  • 在源端,将需要迁移的文件资产保存到分布式数据对象DataObject中,执行流程如下:
  1. 将文件资产拷贝到分布式文件目录下,相关接口与用法详见文件管理与跨设备文件访问。
  2. 使用分布式文件目录下的文件创建Asset资产对象。
  3. 将Asset资产对象作为分布式数据对象的根属性保存。

随后,与普通数据对象的迁移的源端实现相同,可以使用该数据对象加入组网,并进行持久化保存。


跨设备拖拽

跨端拖拽提供跨设备的键鼠共享能力,支持在平板或2in1类型的任意两台设备之间拖拽文件、文本。 

当前系统应用中,文件管理器、浏览器支持拖出;备忘录支持拖入。用户可以体验以下场景:

  • 将A设备文件管理器中的图片拖拽至B设备的备忘录应用。
  • 将A设备备忘录中的文本拖拽至B设备的备忘录应用,并在B设备中使用A设备连接的键盘输入,协同操作。

开发者可以根据实际需求,实现组件的拖入或拖出,即可接入跨设备拖拽。

运作机制

  1. 用户使用鼠标点击组件,触发拖拽事件。
  2. 应用设置拖拽数据。
  3. 系统完成跨设备数据处理,此过程应用不感知。
  4. 用户松手触发拖拽松手事件。
  5. 目的端应用获取并处理拖拽数据。

约束与限制

需同时满足以下条件,才能使用该功能:

  • 设备限制

    HarmonyOS NEXT Developer Preview0及以上版本的平板或2in1设备。

  • 使用限制
    • 双端设备需要登录同一华为账号。
    • 双端设备需要打开Wi-Fi和蓝牙开关,并接入同一个局域网。
    • 打开键鼠穿越开关。
    • 应用本身预置的资源文件(即应用在安装前的HAP包中已经存在的资源文件)不支持跨设备拖拽。
接口说明

在开发具体功能前,请先查阅参考文档,获取详细的接口说明。

  • 拖拽控制:设置组件是否可以响应拖拽事件的属性。组件均需要设置draggable属性才能响应拖拽事件。

    当前部分组件默认支持拖拽控制。应用使用这些组件时,只需要将draggable设置为true,系统将根据组件的支持情况,自动实现onDragStart的写信息或onDrop的读信息。

  • 拖拽事件:组件被鼠标选中后拖拽时触发的事件。

    应用应根据实际需求,实现组件拖入或拖出。

开发示例

拖拽事件通过鼠标左键来操作和响应,开发示例请参考:拖拽事件。

跨设备剪贴板 

剪贴板分为本地剪贴板和跨设备剪贴板,本地剪贴板提供设备内的内容复制粘贴,跨设备剪贴板提供跨设备的内容复制粘贴。

当用户拥有多台设备时,可以通过跨设备剪贴板的功能,在A设备的应用上复制一段文本,粘贴到B设备的应用中,高效地完成多设备间的内容共享。

当开发者正在开发一款浏览器类应用,或是备忘录、笔记、邮件等富文本编辑类应用时,均可接入跨设备剪贴板,提升用户体验。

运作机制

1. 用户在设备A复制数据。

2. 系统剪贴板服务将处理相关数据,并完成数据同步。此过程开发者不感知。

3. 用户在设备B粘贴来自设备A的数据。

约束与限制

设备版本

使用限制

HarmonyOS NEXT Developer Preview0及以上

  • 双端设备需要登录同一华为账号。
  • 双端设备需要打开Wi-Fi和蓝牙开关。

    条件允许时,建议双端设备接入同一个局域网,可提升数据传输的速度。

  • 双端设备在过程中需解锁、亮屏。

HarmonyOS版本3.2及以上

  • 双端设备需要登录同一华为账号。
  • 双端设备需要打开Wi-Fi和蓝牙开关,并接入同一个局域网。
  • 双端设备在过程中需解锁、亮屏。
接口说明

在开发具体功能前,请先查阅参考文档,获取详细的接口说明。

 开发示例

说明

跨设备复制的数据两分钟内有效。

设备A复制数据,写入到剪贴板服务。

import pasteboard from '@ohos.pasteboard';
import { BusinessError } from '@ohos.base';export async function setPasteDataTest(): Promise<void> {let text: string = 'hello world';let pasteData: pasteboard.PasteData = pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, text);let systemPasteBoard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();await systemPasteBoard.setData(pasteData).catch((err: BusinessError) => {console.error(`Failed to set pastedata. Code: ${err.code}, message: ${err.message}`);});
}

设备B粘贴数据,读取剪贴板内容。 

import pasteboard from '@ohos.pasteboard';
import { BusinessError } from '@ohos.base';export async function getPasteDataTest(): Promise<void> {let systemPasteBoard: pasteboard.SystemPasteboard = pasteboard.getSystemPasteboard();systemPasteBoard.getData((err: BusinessError, data: pasteboard.PasteData) => {if (err) {console.error(`Failed to get pastedata. Code: ${err.code}, message: ${err.message}`);return;}// 对pastedata进行处理,获取类型,个数等let recordCount: number = data.getRecordCount(); // 获取剪贴板内record的个数let types: string = data.getPrimaryMimeType(); // 获取剪贴板内数据的类型let primaryText: string = data.getPrimaryText(); // 获取剪贴板内数据的内容});
}

相关文章:

  • 使用cmd来创建数据库和数据库表-简洁步骤
  • 乐企数电发票分布式发票号码生成重复的问题修复思路分享
  • 光敏材料与智能传感技术的能源系统创新研究
  • Redis Desktop Manager 安装教程Windows
  • 数据结构二叉树与二叉搜索树c实现代码
  • 超参数详解:从基础概念到优化策略的全面指南
  • 【nvm管理多个 Node.js 版本】
  • MCP:人工智能时代的HTTP?探索AI通信新标准
  • k8s的volume
  • 苍穹外卖(缓存商品、购物车)
  • OpenVLA:大语言模型用于机器人操控的经典开源作品
  • 使用Aspose.Words将Word转换为HTML时,字体样式丢失问题及解决方法
  • keil 中优化等级的bug
  • 聊聊Spring AI Alibaba的RedisChatMemory
  • JavaEE-多线程实战01
  • 关系型数据库PostgreSQL for Mac 保姆级使用教程
  • 【三大特性】虚表 内存分布
  • AI应用讲座2025年4月笔记
  • 电镀废水资源化利用的工艺介绍
  • Centos 7.6安装redis-6.2.6
  • 王毅会见俄罗斯外长拉夫罗夫
  • 美情报机构攻击中国大型商用密码产品提供商,调查报告公布
  • 周口一乡镇公务员“被老赖”,两年4场官司均败诉,市监局将线索移送公安厅
  • 健康社区“免疫行动”促进计划启动,发布成人预防“保典”
  • 政治局会议:要提高中低收入群体收入,设立服务消费与养老再贷款
  • 这个器官健康的人,不容易得抑郁症