diff --git a/zh-cn/application-dev/ability/Readme-CN.md b/zh-cn/application-dev/ability/Readme-CN.md index c72f9c18af7f90498f5eac50aff092c06fa8ceeb..67afebb60d828b912838a85504884be4f042cd14 100644 --- a/zh-cn/application-dev/ability/Readme-CN.md +++ b/zh-cn/application-dev/ability/Readme-CN.md @@ -8,7 +8,7 @@ - [FormAbility开发指导](fa-formability.md) - Stage模型 - [ServiceExtensionAbility开发指导](stage-serviceextension.md) - - [应用迁移开发指导](stage-ability-continuation.md) + - [跨端迁移开发指导](stage-ability-continuation.md) - [Call调用开发指导](stage-call.md) - 其他 - [WantAgent使用指导](wantagent.md) diff --git a/zh-cn/application-dev/ability/figures/continuation-info.png b/zh-cn/application-dev/ability/figures/continuation-info.png new file mode 100755 index 0000000000000000000000000000000000000000..69c38949921686520378a118df2e9f82912967a8 Binary files /dev/null and b/zh-cn/application-dev/ability/figures/continuation-info.png differ diff --git a/zh-cn/application-dev/ability/stage-ability-continuation.md b/zh-cn/application-dev/ability/stage-ability-continuation.md old mode 100644 new mode 100755 index 1c391834f07554f3744e31e8dff6b02e16d8e83d..b7e67a1b330d7277e522a4cca8038701994e87d6 --- a/zh-cn/application-dev/ability/stage-ability-continuation.md +++ b/zh-cn/application-dev/ability/stage-ability-continuation.md @@ -1,178 +1,232 @@ -# 应用迁移开发指导 +# 跨端迁移开发指导 ## 场景介绍 -迁移的主要工作是实现将应用当前任务,包括页面部分数据、栈信息等,迁移到远端设备。迁移成功后,本端任务将被清除;若失败则不会,可允许再次迁移。 - - +迁移的主要工作是实现将应用当前任务,包括页面控件状态变量、分布式对象等,迁移到远端设备。页面控件状态变量用于同步页面UI数据,分布式对象用于同步内存中的数据。 ## 接口说明 -迁移提供的能力如下,具体的API详见接口文档 +迁移提供的能力如下,具体的API详见[接口文档](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-application-ability.md)。 **表1** 应用迁移API接口功能介绍 |接口名 | 描述| |:------ | :------| -| onContinue(wantParams : {[key: string]: any}) | 迁移发起端在该回调中保存迁移所需要的数据,同时返回是否同意迁移:true表示同意,false表示拒绝。 | +| onContinue(wantParams : {[key: string]: any}): OnContinueResult | 迁移**发起端**在该回调中保存迁移所需要的数据,同时返回是否同意迁移:0表示同意,拒绝返回相应错误码。 | +| onCreate(want: Want,param:LaunchParam): void | 迁移**目标端**在该回调中完成数据恢复,并触发页面恢复。 | +| **enum** OnContinueResult | onContinue的返回值类型:AGREE表示同意;REJECT表示拒绝;MISMATCH表示版本不匹配 | -## 开发步骤 +**图1** 迁移开发示意图 -### 迁移应用 +![continuation_dev](figures/continuation-info.png) +## 开发步骤 +### 迁移应用 1. 配置 - 配置应用支持迁移 - ​ 在config.json中配置continuable字段:true表示支持迁移,false表示不支持,默认为false. + 在module.json5中配置continuable字段:true表示支持迁移,false表示不支持,默认为false。配置为false的应用将被系统识别为无法迁移。 - ``` + ```javascript "continuable": true ``` - ​ 配置为false的应用将无法在任务中心进行迁移。 + * 配置应用启动类型 - ​ 迁移当前只支持多实例应用,需要在在config.json中配置launchType字段为standard + 迁移当前只支持多实例应用,需要在在module.json5中配置launchType字段为standard - ``` + ```javascript "launchType": "standard" ``` + + + * 申请分布式权限 + + 支持跨端迁移的应用需要在module.json5申请分布式权限 DISTRIBUTED_DATASYNC。 + + ```javascript + "requestPermissions": [ + { + "name": "ohos.permission.DISTRIBUTED_DATASYNC" + }, + ``` + + 这个权限需要在应用首次启动的时候弹窗让用户授予,可以通过在ability的onWindowStageCreate中添加如下代码实现 + + ```javascript + requestPermissions = async () => { + let permissions: Array = [ + "ohos.permission.DISTRIBUTED_DATASYNC" + ]; + let needGrantPermission = false + let accessManger = accessControl.createAtManager() + Logger.info("app permission get bundle info") + let bundleInfo = await bundle.getApplicationInfo(BUNDLE_NAME, 0, 100) + Logger.info(`app permission query permission ${bundleInfo.accessTokenId.toString()}`) + for (const permission of permissions) { + Logger.info(`app permission query grant status ${permission}`) + try { + let grantStatus = await accessManger.verifyAccessToken(bundleInfo.accessTokenId, permission) + if (grantStatus === PERMISSION_REJECT) { + needGrantPermission = true + break; + } + } catch (err) { + Logger.error(`app permission query grant status error ${permission} ${JSON.stringify(err)}`) + needGrantPermission = true + break; + } + } + if (needGrantPermission) { + Logger.info("app permission needGrantPermission") + try { + await this.context.requestPermissionsFromUser(permissions) + } catch (err) { + Logger.error(`app permission ${JSON.stringify(err)}`) + } + } else { + Logger.info("app permission already granted") + } + } + ``` + -2. 实现onContinue接口 +2. 实现onContinue接口 - 导入模块 + onContinue接口在**发起端**被调用,主要用于在迁移发起时,通知开发者保存控件状态变量和内存中数据,准备迁移。当应用准备完成后,需要返回OnContinueResult.AGREE(0)表示同意迁移,否则返回相应的错误码拒绝迁移。如果不实现该接口,系统将默认为拒绝迁移。 - ``` + 导入模块 + + ```javascript import Ability from '@ohos.application.Ability'; + import AbilityConstant from '@ohos.application.AbilityConstant'; ``` - - - 要实现迁移,此接口必须实现并返回true,否则默认为拒绝迁移。 + + - 要实现迁移,此接口必须实现并返回AGREE,否则默认为拒绝迁移。 - 示例 ```javascript - onContinue(wantParams : {[key: string]: any}) { - console.log("MainAbility onContinue") - return true; - } + onContinue(wantParam : {[key: string]: any}) { + Logger.info("onContinue using distributedObject") + // set user input data into want params + wantParam["input"] = AppStorage.Get('ContinueInput'); + Logger.info(`onContinue input = ${wantParam["input"]}`); + return AbilityConstant.OnContinueResult.AGREE + } ``` 3. 在onCreate接口中实现迁移逻辑 - - 远端设备上,在onCreate中根据launchReason判断该次启动是否为迁移LaunchReason.CONTINUATION(3) + onCreate接口在迁移**目标端**被调用,在目标端ability被拉起时,通知开发者同步已保存的内存数据和控件状态,完成后触发页面的恢复。如果不实现该接口中迁移相关逻辑,ability将会作为普通的启动方式拉起,无法恢复页面。 + - 远端设备上,在onCreate中根据launchReason判断该次启动是否为迁移LaunchReason.CONTINUATION + - 完成数据恢复后,开发者需要调用**restoreWindowStage**来触发页面恢复。 - + * 示例 ```javascript - onCreate(want , launchParam) { - // Ability is creating, initialize resources for this ability - console.log("MainAbility onCreate", launchParam.launchReason); - if (launchParam.launchReason == LaunchReason.CONTINUATION) { - this.contentStorage = new ContenStorage(); + onCreate(want, launchParam) { + Logger.info(`MainAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`) + globalThis.abilityWant = want; + if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) { + let input = want.parameters.input // get user data from want params + AppStorage.SetOrCreate('ContinueInput', input) + Logger.info(`onCreate for continuation sessionId: ${this.sessionId}`) + + this.contentStorage = new ContentStorage(); this.context.restoreWindowStage(this.contentStorage); } } - ``` ### 迁移数据 -1. 使用自定义数据 +1. 使用分布式对象 - - wantParams中可以填写key-value形式自定义数据,key类型string,填充的数据将随want被传输到远端,用于携带一些简单、轻量的数据。 + 分布式数据对象提供了与本地变量类似的操作,实现两个设备的数据同步,当设备1的应用A的分布式数据对象增、删、改数据后,设备2的应用A也可以获取到对应的数据变化,同时还能监听数据变更以及对端数据对象的上下线。用法详见[分布式对象指导文档](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/database/database-distributedobject-guidelines.md)。 + 迁移场景中,分布式对象(distributedDataObject)主要用于将本机内存数据同步到目标设备。 - ```javascript - onContinue(wantParams : {[key: string]: any}) { - console.log("Continue My Data") - wantParams["myData"] = "my1234567"; - return true; - } - ``` - - - 同时在远端判断如果是迁移,可以从want.parameters中取出在发起端保存的自定义数据。 + - 发起端在onContinue中,将待迁移的数据存入分布式对象中,然后设置好session id,并通过wantParams将session id传到远端设备。 - ```javascript - onCreate(want , launchParam) { - if (launchParam.launchReason == LaunchReason.CONTINUATION) { - console.log("onCreate LaunchReason = CONTINUATION",want.parameters["myData"]); // my1234567 - ... - this.context.restoreWindowStage(this.contentStorage); - } + ```javascript + import Ability from '@ohos.application.Ability'; + import distributedObject from '@ohos.data.distributedDataObject'; + + var g_object = distributedObject.createDistributedObject({name:undefined}); + + export default class MainAbility extends Ability { + contentStorage : ContenStorage + sessionId : string; + + onContinue(wantParam : {[key: string]: any}) { + Logger.info("onContinue using distributedObject") + this.sessionId = distributedObject.genSessionId(); + //set distributed data object session id + g_object.setSessionId(this.sessionId); + g_object.name = "Amy"; + // set session id into want params + wantParam["session"] = this.sessionId; + return AbilityConstant.OnContinueResult.AGREE } - ``` + ``` + - 目标设备在onCreate中,取出发起端传过来的session id,建立分布式对象并关联该session id,这样就能实现分布式对象的同步。需要注意的是,在调用restoreWindowStage之前,迁移需要的分布式对象必须全部关联完,保证能够获取到正确的数据。 - -2. 使用分布式对象 - - 使用分布式对象可以传输更多的数据到远端设备,更多用法详见分布式对象接口文档。 - - - 发起端在onContinue中,将待迁移的数据存入分布式对象中,然后设置好session id,并通过wantParams将session id传到远端设备。 - - - - 远端设备在onCreate中,取出发起端传过来的session id,建立分布式对象并关联该session id,这样就能实现分布式对象的同步。需要注意的是,在调用restoreWindowStage之前,迁移需要的分布式对象必须全部关联完,保证能够获取到正确的数据。 - - - * 示例 - ```javascript - import Ability from '@ohos.application.Ability'; - import distributedObject from '@ohos.data.distributedDataObject'; - - var g_object = distributedObject.createDistributedObject({name:undefined}); - - export default class MainAbility extends Ability { - contentStorage : ContenStorage - sessionId : string; - onCreate(want , launchParam) { - if (launchParam.launchReason == 3) { - this.sessionId = want.parameters["session"] // 取出session id - - function statusCallback(sessionId, networkid, status) { - console.info("object status change sessionId: " + sessionId + " status: " + status + - "g_object.name: " + g_object.name); // 回调中可以取到同步过来的分布式对象内容 name = Amy - } - g_object.on("status", statusCallback); // 注册分布式对象同步结果的监听 - - g_object.setSessionId(this.sessionId); // 将本地分布式对象也关联发起端的session id - - this.contentStorage = new ContenStorage(); - this.context.restoreWindowStage(this.contentStorage); - } - } - - onContinue(wantParams : {[key: string]: any}) { - console.log("using distributedObject") - this.sessionId = "654321"; - g_object.setSessionId(this.sessionId); //1 设置分布式对象的session id - g_object.name = "Amy"; // 填入数据 - wantParams["session"] = this.sessionId; // 将session id 通过want传到远端 - return true; - } + import Ability from '@ohos.application.Ability'; + import distributedObject from '@ohos.data.distributedDataObject'; + + var g_object = distributedObject.createDistributedObject({name:undefined}); + + export default class MainAbility extends Ability { + contentStorage : ContentStorage + sessionId : string; + + statusCallback(sessionId, networkid, status) { + Logger.info(`continuation object status change, sessionId: ${sessionId}, status: ${status}, g_object.name: ${g_object.name}`) + } + + onCreate(want, launchParam) { + Logger.info(`MainAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`) + if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) { + // get distributed data object session id from want params + this.sessionId = want.parameters.session + Logger.info(`onCreate for continuation sessionId: ${this.sessionId}`) + + g_object.on("status", this.statusCallback); + // set session id, so it will sync data from remote device + g_object.setSessionId(this.sessionId); + + this.contentStorage = new ContentStorage(); + this.context.restoreWindowStage(this.contentStorage); + } + } + } ``` - + - +以上完整的示例见sample