1# 跨端迁移开发指导 2 3## 场景介绍 4 5迁移的主要工作是实现将应用当前任务,包括页面控件状态变量、分布式对象等,迁移到远端设备。页面控件状态变量用于同步页面UI数据,分布式对象用于同步内存中的数据。 6 7## 接口说明 8 9迁移提供的能力如下,具体的API详见[接口文档](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/reference/apis/js-apis-application-ability.md)。 10 11**表1** 应用迁移API接口功能介绍 12 13|接口名 | 描述| 14|:------ | :------| 15| onContinue(wantParam : {[key: string]: any}): OnContinueResult | 迁移**发起端**在该回调中保存迁移所需要的数据,同时返回是否同意迁移:0表示同意,拒绝返回相应错误码。 | 16| onCreate(want: Want,param:LaunchParam): void | 迁移**目标端**在该回调中完成数据恢复,并触发页面恢复。 | 17| **enum** OnContinueResult | onContinue的返回值类型:AGREE表示同意;REJECT表示拒绝;MISMATCH表示版本不匹配 | 18 19 20 21**图1** 迁移开发示意图 22 23 24 25## 开发步骤 26 27### 迁移应用 28 291. 配置 30 31 - 配置应用支持迁移 32 33 在module.json5中配置continuable字段:true表示支持迁移,false表示不支持,默认为false。配置为false的应用将被系统识别为无法迁移。 34 35 ```javascript 36 "continuable": true 37 ``` 38 39 40 41 * 配置应用启动类型 42 43 迁移当前只支持多实例应用,需要在在module.json5中配置launchType字段为standard。 44 45 ```javascript 46 "launchType": "standard" 47 ``` 48 49 50 51 * 申请分布式权限 52 53 支持跨端迁移的应用需要在module.json5申请分布式权限 DISTRIBUTED_DATASYNC。 54 55 ```javascript 56 "requestPermissions": [ 57 { 58 "name": "ohos.permission.DISTRIBUTED_DATASYNC" 59 }, 60 ``` 61 62 这个权限需要在应用首次启动的时候弹窗让用户授予,可以通过在ability的onWindowStageCreate中添加如下代码实现: 63 64 ```javascript 65 requestPermissions = async () => { 66 let permissions: Array<string> = [ 67 "ohos.permission.DISTRIBUTED_DATASYNC" 68 ]; 69 let needGrantPermission = false 70 let accessManger = accessControl.createAtManager() 71 Logger.info("app permission get bundle info") 72 let bundleInfo = await bundle.getApplicationInfo(BUNDLE_NAME, 0, 100) 73 Logger.info(`app permission query permission ${bundleInfo.accessTokenId.toString()}`) 74 for (const permission of permissions) { 75 Logger.info(`app permission query grant status ${permission}`) 76 try { 77 let grantStatus = await accessManger.verifyAccessToken(bundleInfo.accessTokenId, permission) 78 if (grantStatus === PERMISSION_REJECT) { 79 needGrantPermission = true 80 break; 81 } 82 } catch (err) { 83 Logger.error(`app permission query grant status error ${permission} ${JSON.stringify(err)}`) 84 needGrantPermission = true 85 break; 86 } 87 } 88 if (needGrantPermission) { 89 Logger.info("app permission needGrantPermission") 90 try { 91 await this.context.requestPermissionsFromUser(permissions) 92 } catch (err) { 93 Logger.error(`app permission ${JSON.stringify(err)}`) 94 } 95 } else { 96 Logger.info("app permission already granted") 97 } 98 } 99 ``` 100 101 102 1032. 实现onContinue接口 104 105 onContinue接口在**发起端**被调用,主要用于在迁移发起时,通知开发者保存控件状态变量和内存中数据,准备迁移。当应用准备完成后,需要返回OnContinueResult.AGREE(0)表示同意迁移,否则返回相应的错误码拒绝迁移。如果不实现该接口,系统将默认为拒绝迁移。 106 107 导入模块 108 109 ```javascript 110 import Ability from '@ohos.application.Ability'; 111 import AbilityConstant from '@ohos.application.AbilityConstant'; 112 ``` 113 114 - 要实现迁移,此接口必须实现并返回AGREE,否则默认为拒绝迁移。 115 116 117 - 示例 118 119 ```javascript 120 onContinue(wantParam : {[key: string]: any}) { 121 Logger.info("onContinue using distributedObject") 122 // set user input data into want params 123 wantParam["input"] = AppStorage.Get<string>('ContinueInput'); 124 Logger.info(`onContinue input = ${wantParam["input"]}`); 125 return AbilityConstant.OnContinueResult.AGREE 126 } 127 ``` 128 129 130 1313. 在onCreate接口中实现迁移逻辑 132 133 onCreate接口在迁移**目标端**被调用,在目标端ability被拉起时,通知开发者同步已保存的内存数据和控件状态,完成后触发页面的恢复。如果不实现该接口中迁移相关逻辑,ability将会作为普通的启动方式拉起,无法恢复页面。 134 135 - 远端设备上,在onCreate中根据launchReason判断该次启动是否为迁移LaunchReason.CONTINUATION 136 137 138 - 完成数据恢复后,开发者需要调用**restoreWindowStage**来触发页面恢复。 139 140 141 * 示例 142 143 ```javascript 144 onCreate(want, launchParam) { 145 Logger.info(`MainAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`) 146 globalThis.abilityWant = want; 147 if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) { 148 let input = want.parameters.input // get user data from want params 149 AppStorage.SetOrCreate<string>('ContinueInput', input) 150 Logger.info(`onCreate for continuation sessionId: ${this.sessionId}`) 151 152 this.contentStorage = new ContentStorage(); 153 this.context.restoreWindowStage(this.contentStorage); 154 } 155 } 156 ``` 157 158 159 160### 迁移数据 161 1621. 使用分布式对象 163 164 分布式数据对象提供了与本地变量类似的操作,实现两个设备的数据同步,当设备1的应用A的分布式数据对象增、删、改数据后,设备2的应用A也可以获取到对应的数据变化,同时还能监听数据变更以及对端数据对象的上下线。用法详见[分布式对象指导文档](https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/database/database-distributedobject-guidelines.md)。 165 166 迁移场景中,分布式对象(distributedDataObject)主要用于将本机内存数据同步到目标设备。 167 168 - 发起端在onContinue中,将待迁移的数据存入分布式对象中,然后设置好session id,并通过wantParam将session id传到远端设备。 169 170 ```javascript 171 import Ability from '@ohos.application.Ability'; 172 import distributedObject from '@ohos.data.distributedDataObject'; 173 174 var g_object = distributedObject.createDistributedObject({name:undefined}); 175 176 export default class MainAbility extends Ability { 177 contentStorage : ContentStorage 178 sessionId : string; 179 180 onContinue(wantParam : {[key: string]: any}) { 181 Logger.info("onContinue using distributedObject") 182 this.sessionId = distributedObject.genSessionId(); 183 //set distributed data object session id 184 g_object.setSessionId(this.sessionId); 185 g_object.name = "Amy"; 186 // set session id into want params 187 wantParam["session"] = this.sessionId; 188 return AbilityConstant.OnContinueResult.AGREE 189 } 190 191 ``` 192 193 - 目标设备在onCreate中,取出发起端传过来的session id,建立分布式对象并关联该session id,这样就能实现分布式对象的同步。需要注意的是,在调用restoreWindowStage之前,迁移需要的分布式对象必须全部关联完,保证能够获取到正确的数据。 194 195 ```javascript 196 import Ability from '@ohos.application.Ability'; 197 import distributedObject from '@ohos.data.distributedDataObject'; 198 199 var g_object = distributedObject.createDistributedObject({name:undefined}); 200 201 export default class MainAbility extends Ability { 202 contentStorage : ContentStorage 203 sessionId : string; 204 205 statusCallback(sessionId, networkid, status) { 206 Logger.info(`continuation object status change, sessionId: ${sessionId}, status: ${status}, g_object.name: ${g_object.name}`) 207 } 208 209 onCreate(want, launchParam) { 210 Logger.info(`MainAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`) 211 if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) { 212 // get distributed data object session id from want params 213 this.sessionId = want.parameters.session 214 Logger.info(`onCreate for continuation sessionId: ${this.sessionId}`) 215 216 g_object.on("status", this.statusCallback); 217 // set session id, so it will sync data from remote device 218 g_object.setSessionId(this.sessionId); 219 220 this.contentStorage = new ContentStorage(); 221 this.context.restoreWindowStage(this.contentStorage); 222 } 223 } 224 } 225 ``` 226 227 228 229以上完整的示例见sample 230 231 232 233