“d00252688604edfd07d0e11a05d3a2b7cf05bb3d”上不存在“git@gitcode.net:openanolis/cloud-kernel.git”
hop-multi-device-collaboration.md 24.4 KB
Newer Older
Z
zengyawen 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
# 多端协同(仅对系统应用开放)


## 功能描述

多端协同主要包括如下场景:

- [通过跨设备启动UIAbility和ServiceExtensionAbility组件实现多端协同(无返回数据)](#通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据)

- [通过跨设备启动UIAbility组件实现多端协同(获取返回数据)](#通过跨设备启动uiability组件实现多端协同获取返回数据)

- [通过跨设备连接ServiceExtensionAbility组件实现多端协同](#通过跨设备连接serviceextensionability组件实现多端协同)

- [通过跨设备Call调用实现多端协同](#通过跨设备call调用实现多端协同)

16
- [退出由跨设备拉起的serviceextensionability组件](#退出由跨设备拉起的serviceextensionability组件)
X
xinking129 已提交
17

Z
zengyawen 已提交
18 19 20 21 22 23 24 25 26 27 28 29 30

## 多端协同流程

多端协同流程如下图所示。

  **图1** 多端协同流程图  
![hop-multi-device-collaboration](figures/hop-multi-device-collaboration.png)


## 约束限制

- 由于“多端协同任务管理”能力尚未具备,开发者当前只能通过开发系统应用获取设备列表,不支持三方应用接入。

Z
zhongjianfei 已提交
31
- 多端协同需遵循[分布式跨设备组件启动规则](component-startup-rules.md#分布式跨设备组件启动规则)
Z
zengyawen 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

- 为了获得最佳体验,使用want传输的数据建议在100KB以下。


## 通过跨设备启动UIAbility和ServiceExtensionAbility组件实现多端协同(无返回数据)

在设备A上通过发起端应用提供的启动按钮,启动设备B上指定的UIAbility。


### 接口说明

  **表1** 跨设备启动API接口功能介绍

| **接口名** | **描述** |
| -------- | -------- |
| startAbility(want: Want, callback: AsyncCallback<void>): void; | 启动UIAbility和ServiceExtensionAbility(callback形式)。 |


### 开发步骤

52
1. 需要申请`ohos.permission.DISTRIBUTED_DATASYNC`权限,配置方式请参见[配置文件权限声明](../security/accesstoken-guidelines.md#配置文件权限声明)
Z
zengyawen 已提交
53

54
2. 同时需要在应用首次启动时弹窗向用户申请授权,使用方式请参见[向用户申请授权](../security/accesstoken-guidelines.md#向用户申请授权)
Z
zengyawen 已提交
55 56

3. 获取目标设备的设备ID。
57

Z
zengyawen 已提交
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
   ```ts
   import deviceManager from '@ohos.distributedHardware.deviceManager';
   
   let dmClass;
   function initDmClass() {
       // 其中createDeviceManager接口为系统API
       deviceManager.createDeviceManager('ohos.samples.demo', (err, dm) => {
           if (err) {
               // ...
               return
           }
           dmClass = dm
       })
   }
   function getRemoteDeviceId() {
       if (typeof dmClass === 'object' && dmClass !== null) {
           let list = dmClass.getTrustedDeviceListSync()
           if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') {
               console.info('EntryAbility onButtonClick getRemoteDeviceId err: list is null')
               return;
           }
           return list[0].deviceId
       } else {
           console.info('EntryAbility onButtonClick getRemoteDeviceId err: dmClass is null')
       }
   }
   ```

86
4. 设置目标组件参数,调用[startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability)接口,启动UIAbility或ServiceExtensionAbility。
87

Z
zengyawen 已提交
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
   ```ts
   let want = {
       deviceId: getRemoteDeviceId(), 
       bundleName: 'com.example.myapplication',
       abilityName: 'FuncAbility',
       moduleName: 'module1', // moduleName非必选
   }
   // context为发起端UIAbility的AbilityContext
   this.context.startAbility(want).then(() => {
       // ...
   }).catch((err) => {
       // ...
   })
   ```


## 通过跨设备启动UIAbility组件实现多端协同(获取返回数据)

在设备A上通过应用提供的启动按钮,启动设备B上指定的UIAbility,当设备B上的UIAbility退出后,会将返回值发回设备A上的发起端应用。


### 接口说明

  **表2** 跨设备启动,返回结果数据API接口功能描述

| 接口名 | 描述 |
| -------- | -------- |
| startAbilityForResult(want: Want, callback: AsyncCallback<AbilityResult>): void; | 启动UIAbility并在该Ability退出的时候返回执行结果(callback形式)。 |
| terminateSelfWithResult(parameter: AbilityResult, callback: AsyncCallback<void>): void; | 停止UIAbility,配合startAbilityForResult使用,返回给接口调用方AbilityResult信息(callback形式)。 |
| terminateSelfWithResult(parameter: AbilityResult): Promise<void>; | 停止UIAbility,配合startAbilityForResult使用,返回给接口调用方AbilityResult信息(promise形式)。 |


### 开发步骤

122
1. 需要申请`ohos.permission.DISTRIBUTED_DATASYNC`权限,配置方式请参见[配置文件权限声明](../security/accesstoken-guidelines.md#配置文件权限声明)
Z
zengyawen 已提交
123

124
2. 同时需要在应用首次启动时弹窗向用户申请授权,使用方式请参见[向用户申请授权](../security/accesstoken-guidelines.md#向用户申请授权)
Z
zengyawen 已提交
125 126

3. 在发起端设置目标组件参数,调用startAbilityForResult()接口启动目标端UIAbility,异步回调中的data用于接收目标端UIAbility停止自身后返回给调用方UIAbility的信息。getRemoteDeviceId方法参照[通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据](#通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据)
127

Z
zengyawen 已提交
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
   ```ts
   let want = {
       deviceId: getRemoteDeviceId(), 
       bundleName: 'com.example.myapplication',
       abilityName: 'FuncAbility',
       moduleName: 'module1', // moduleName非必选
   }
   // context为发起端UIAbility的AbilityContext
   this.context.startAbilityForResult(want).then((data) => {
       // ...
   }).catch((err) => {
       // ...
   })
   ```

4. 在目标端UIAbility任务完成后,调用terminateSelfWithResult()方法,将数据返回给发起端的UIAbility。
144

Z
zengyawen 已提交
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
   ```ts
   const RESULT_CODE: number = 1001;
   let abilityResult = {
       resultCode: RESULT_CODE,
       want: {
           bundleName: 'com.example.myapplication',
           abilityName: 'FuncAbility',
           moduleName: 'module1',
       },
   }
   // context为目标端UIAbility的AbilityContext
   this.context.terminateSelfWithResult(abilityResult, (err) => {
       // ...
   });
   ```

5. 发起端UIAbility接收到目标端UIAbility返回的信息,对其进行处理。
162

Z
zengyawen 已提交
163 164 165 166 167
   ```ts
   const RESULT_CODE: number = 1001;
   
   // ...
   
168
   // context为调用方UIAbility的UIAbilityContext
Z
zengyawen 已提交
169 170 171 172 173 174 175 176 177 178 179 180 181 182
   this.context.startAbilityForResult(want).then((data) => {
       if (data?.resultCode === RESULT_CODE) {
           // 解析目标端UIAbility返回的信息
           let info = data.want?.parameters?.info
           // ...
       }
   }).catch((err) => {
       // ...
   })
   ```


## 通过跨设备连接ServiceExtensionAbility组件实现多端协同

Z
zhongjianfei 已提交
183
系统应用可以通过[connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextconnectserviceextensionability)跨设备连接一个服务,实现跨设备远程调用。比如:分布式游戏场景,平板作为遥控器,智慧屏作为显示器。
Z
zengyawen 已提交
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198


### 接口说明

  **表3** 跨设备连接API接口功能介绍

| 接口名 | 描述 |
| -------- | -------- |
| connectServiceExtensionAbility(want: Want, options: ConnectOptions): number; | 连接ServiceExtensionAbility。 |
| disconnectServiceExtensionAbility(connection: number, callback:AsyncCallback<void>): void; | 断开连接(callback形式)。 |
| disconnectServiceExtensionAbility(connection: number): Promise<void>; | 断开连接(promise形式)。 |


### 开发步骤

199 200 201
1. 需要申请`ohos.permission.DISTRIBUTED_DATASYNC`权限,配置方式请参见[配置文件权限声明](../security/accesstoken-guidelines.md#配置文件权限声明)

2. 同时需要在应用首次启动时弹窗向用户申请授权,使用方式请参见[向用户申请授权](../security/accesstoken-guidelines.md#向用户申请授权)
Z
zengyawen 已提交
202 203 204 205 206

3. 如果已有后台服务,请直接进入下一步;如果没有,则[实现一个后台服务](serviceextensionability.md#实现一个后台服务仅对系统应用开放)

4. 连接一个后台服务。
   - 实现IAbilityConnection接口。IAbilityConnection提供了以下方法供开发者实现:onConnect()是用来处理连接Service成功的回调,onDisconnect()是用来处理Service异常终止的回调,onFailed()是用来处理连接Service失败的回调。
207
   - 设置目标组件参数,包括目标设备ID、Bundle名称、Ability名称。
Z
zengyawen 已提交
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
   - 调用connectServiceExtensionAbility发起连接。
   - 连接成功,收到目标设备返回的服务句柄。
   - 进行跨设备调用,获得目标端服务返回的结果。
     
      ```ts
      import rpc from '@ohos.rpc';
      
      const REQUEST_CODE = 99;
      let want = {
          "deviceId": getRemoteDeviceId(), 
          "bundleName": "com.example.myapplication",
          "abilityName": "ServiceExtAbility"
      };
      let options = {
          onConnect(elementName, remote) {
              console.info('onConnect callback');
              if (remote === null) {
                  console.info(`onConnect remote is null`);
                  return;
              }
              let option = new rpc.MessageOption();
              let data = new rpc.MessageParcel();
              let reply = new rpc.MessageParcel();
              data.writeInt(1);
              data.writeInt(99);  // 开发者可发送data到目标端应用进行相应操作
      
              // @param code 表示客户端发送的服务请求代码。
              // @param data 表示客户端发送的{@link MessageParcel}对象。
              // @param reply 表示远程服务发送的响应消息对象。
              // @param options 指示操作是同步的还是异步的。
              // 
              // @return 如果操作成功返回{@code true}; 否则返回 {@code false}。
              remote.sendRequest(REQUEST_CODE, data, reply, option).then((ret) => {
                  let msg = reply.readInt();   // 在成功连接的情况下,会收到来自目标端返回的信息(100)
                  console.info(`sendRequest ret:${ret} msg:${msg}`);
              }).catch((error) => {
                  console.info('sendRequest failed');
              });
          },
          onDisconnect(elementName) {
zyjhandsome's avatar
zyjhandsome 已提交
248
              console.info('onDisconnect callback');
Z
zengyawen 已提交
249 250
          },
          onFailed(code) {
zyjhandsome's avatar
zyjhandsome 已提交
251
              console.info('onFailed callback');
Z
zengyawen 已提交
252 253 254 255 256 257 258 259 260
          }
      }
      // 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
      let connectionId = this.context.connectServiceExtensionAbility(want, options);
      ```

      getRemoteDeviceId方法参照[通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据](#通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据)。

5. 断开连接。调用disconnectServiceExtensionAbility()断开与后台服务的连接。
261

Z
zengyawen 已提交
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
   ```ts
   let connectionId = 1 // 在通过connectServiceExtensionAbility绑定服务时返回的Id
   this.context.disconnectServiceExtensionAbility(connectionId).then((data) => {
       console.info('disconnectServiceExtensionAbility success');
   }).catch((error) => {
       console.error('disconnectServiceExtensionAbility failed');
   })
   ```


## 通过跨设备Call调用实现多端协同

跨设备Call调用的基本原理与设备内Call调用相同,请参见[通过Call调用实现UIAbility交互(仅对系统应用开放)](uiability-intra-device-interaction.md#通过call调用实现uiability交互仅对系统应用开放)

下面介绍跨设备Call调用实现多端协同的方法。


### 接口说明

  **表4** Call API接口功能介绍

| 接口名 | 描述 |
| -------- | -------- |
| startAbilityByCall(want: Want): Promise<Caller>; | 启动指定UIAbility至前台或后台,同时获取其Caller通信接口,调用方可使用Caller与被启动的Ability进行通信。 |
| on(method: string, callback: CalleeCallBack): void | 通用组件Callee注册method对应的callback方法。 |
| off(method: string): void | 通用组件Callee解注册method的callback方法。 |
D
dy_study 已提交
288 289
| call(method: string, data: rpc.Parcelable): Promise<void> | 向通用组件Callee发送约定序列化数据。 |
| callWithResult(method: string, data: rpc.Parcelable): Promise<rpc.MessageSequence> | 向通用组件Callee发送约定序列化数据, 并将Callee返回的约定序列化数据带回。 |
Z
zengyawen 已提交
290 291 292 293 294 295
| release(): void | 释放通用组件的Caller通信接口。 |
| on(type: "release", callback: OnReleaseCallback): void | 注册通用组件通信断开监听通知。 |


### 开发步骤

296 297 298
1. 需要申请`ohos.permission.DISTRIBUTED_DATASYNC`权限,配置方式请参见[配置文件权限声明](../security/accesstoken-guidelines.md#配置文件权限声明)

2. 同时需要在应用首次启动时弹窗向用户申请授权,使用方式请参见[向用户申请授权](../security/accesstoken-guidelines.md#向用户申请授权)
Z
zengyawen 已提交
299 300

3. 创建被调用端UIAbility。
301
     被调用端UIAbility需要实现指定方法的数据接收回调函数、数据的序列化及反序列化方法。在需要接收数据期间,通过on接口注册监听,无需接收数据时通过off接口解除监听。
Z
zengyawen 已提交
302

303 304
     1. 配置UIAbility的启动模式。
         配置module.json5,将CalleeAbility配置为单实例"singleton"。
Z
zengyawen 已提交
305

306 307 308
         | Json字段 | 字段说明 |
         | -------- | -------- |
         | “launchType” | Ability的启动模式,设置为"singleton"类型。 |
Z
zengyawen 已提交
309

310
         UIAbility配置标签示例如下:
Z
zengyawen 已提交
311

312 313 314 315
         
         ```json
         "abilities":[{
             "name": ".CalleeAbility",
316
             "srcEntry": "./ets/CalleeAbility/CalleeAbility.ts",
317 318 319 320
             "launchType": "singleton",
             "description": "$string:CalleeAbility_desc",
             "icon": "$media:icon",
             "label": "$string:CalleeAbility_label",
321
             "exported": true
322 323 324 325 326 327 328 329 330
         }]
         ```
     2. 导入UIAbility模块。
        
         ```ts
         import Ability from '@ohos.app.ability.UIAbility';
         ```
     3. 定义约定的序列化数据。
         调用端及被调用端发送接收的数据格式需协商一致,如下示例约定数据由number和string组成。
Z
zengyawen 已提交
331 332

         
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
         ```ts
         export default class MyParcelable {
             num: number = 0;
             str: string = "";
         
             constructor(num, string) {
                 this.num = num;
                 this.str = string;
             }
         
             marshalling(messageSequence) {
                 messageSequence.writeInt(this.num);
                 messageSequence.writeString(this.str);
                 return true;
             }
         
             unmarshalling(messageSequence) {
                 this.num = messageSequence.readInt();
                 this.str = messageSequence.readString();
                 return true;
             }
         }
         ```
     4. 实现Callee.on监听及Callee.off解除监听。
           如下示例在Ability的onCreate注册MSG_SEND_METHOD监听,在onDestroy取消监听,收到序列化数据后作相应处理并返回。应用开发者根据实际业务需要做相应处理。
           
         ```ts
         const TAG: string = '[CalleeAbility]';
         const MSG_SEND_METHOD: string = 'CallSendMsg';
         
         function sendMsgCallback(data) {
             console.info('CalleeSortFunc called');
         
             // 获取Caller发送的序列化数据
             let receivedData = new MyParcelable(0, '');
             data.readParcelable(receivedData);
             console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`);
         
             // 作相应处理
             // 返回序列化数据result给Caller
             return new MyParcelable(receivedData.num + 1, `send ${receivedData.str} succeed`);
         }
         
         export default class CalleeAbility extends Ability {
             onCreate(want, launchParam) {
                 try {
                     this.callee.on(MSG_SEND_METHOD, sendMsgCallback);
                 } catch (error) {
                     console.info(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`);
                 }
             }
         
             onDestroy() {
                 try {
                     this.callee.off(MSG_SEND_METHOD);
                 } catch (error) {
                     console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`);
                 }
             }
         }
         ```
Z
zengyawen 已提交
394 395 396 397 398

4. 获取Caller接口,访问被调用端UIAbility。
   1. 导入UIAbility模块。
      
       ```ts
zyjhandsome's avatar
zyjhandsome 已提交
399
       import Ability from '@ohos.app.ability.UIAbility';
Z
zengyawen 已提交
400 401
       ```
   2. 获取Caller通信接口。
402
       Ability的context属性实现了startAbilityByCall方法,用于获取指定通用组件的Caller通信接口。如下示例通过this.context获取Ability实例的context属性,使用startAbilityByCall拉起Callee被调用端并获取Caller通信接口,注册Caller的onRelease和onRemoteStateChange监听。应用开发者根据实际业务需要做相应处理。
Z
zengyawen 已提交
403 404 405 406

       
       ```ts
       async onButtonGetRemoteCaller() {
zyjhandsome's avatar
zyjhandsome 已提交
407 408
           var caller = undefined;
           var context = this.context;
Z
zengyawen 已提交
409 410 411 412 413 414 415
       
           context.startAbilityByCall({
               deviceId: getRemoteDeviceId(),
               bundleName: 'com.samples.CallApplication',
               abilityName: 'CalleeAbility'
           }).then((data) => {
               if (data != null) {
zyjhandsome's avatar
zyjhandsome 已提交
416 417
                   caller = data;
                   console.info('get remote caller success');
Z
zengyawen 已提交
418 419
                   // 注册caller的release监听
                   caller.onRelease((msg) => {
zyjhandsome's avatar
zyjhandsome 已提交
420
                       console.info(`remote caller onRelease is called ${msg}`);
Z
zengyawen 已提交
421
                   })
zyjhandsome's avatar
zyjhandsome 已提交
422
                   console.info('remote caller register OnRelease succeed');
423 424 425 426 427 428 429 430
                   // 注册caller的协同场景下跨设备组件状态变化监听通知
                   try {
                        caller.onRemoteStateChange((str) => {
                            console.log('Remote state changed ' + str);
                        });
                    } catch (error) {
                        console.log('Caller.onRemoteStateChange catch error, error.code: ${JSON.stringify(error.code)}, error.message: ${JSON.stringify(error.message)}');
                    }
Z
zengyawen 已提交
431 432
               }
           }).catch((error) => {
zyjhandsome's avatar
zyjhandsome 已提交
433
               console.error(`get remote caller failed with ${error}`);
Z
zengyawen 已提交
434 435 436 437 438 439 440
           })
       }
       ```

       getRemoteDeviceId方法参照[通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据](#通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据)。

5. 向被调用端UIAbility发送约定序列化数据。
D
dy_study 已提交
441
   1. 向被调用端发送Parcelable数据有两种方式,一种是不带返回值,一种是获取被调用端返回的数据,method以及序列化数据需要与被调用端协商一致。如下示例调用Call接口,向Callee被调用端发送数据。
Z
zengyawen 已提交
442 443
      
       ```ts
zyjhandsome's avatar
zyjhandsome 已提交
444
       const MSG_SEND_METHOD: string = 'CallSendMsg';
Z
zengyawen 已提交
445 446
       async onButtonCall() {
           try {
D
dy_study 已提交
447
               let msg = new MyParcelable(1, 'origin_Msg');
zyjhandsome's avatar
zyjhandsome 已提交
448
               await this.caller.call(MSG_SEND_METHOD, msg);
Z
zengyawen 已提交
449
           } catch (error) {
zyjhandsome's avatar
zyjhandsome 已提交
450
               console.info(`caller call failed with ${error}`);
Z
zengyawen 已提交
451 452 453 454 455 456
           }
       }
       ```
   2. 如下示例调用CallWithResult接口,向Callee被调用端发送待处理的数据originMsg,并将’CallSendMsg’方法处理完毕的数据赋值给backMsg。
      
       ```ts
zyjhandsome's avatar
zyjhandsome 已提交
457 458 459
       const MSG_SEND_METHOD: string = 'CallSendMsg';
       originMsg: string = '';
       backMsg: string = '';
Z
zengyawen 已提交
460 461
       async onButtonCallWithResult(originMsg, backMsg) {
           try {
D
dy_study 已提交
462
               let msg = new MyParcelable(1, originMsg);
zyjhandsome's avatar
zyjhandsome 已提交
463 464
               const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg);
               console.info('caller callWithResult succeed');
Z
zengyawen 已提交
465
       
D
dy_study 已提交
466 467
               let result = new MyParcelable(0, '');
               data.readParcelable(result);
zyjhandsome's avatar
zyjhandsome 已提交
468 469
               backMsg(result.str);
               console.info(`caller result is [${result.num}, ${result.str}]`);
Z
zengyawen 已提交
470
           } catch (error) {
zyjhandsome's avatar
zyjhandsome 已提交
471
               console.info(`caller callWithResult failed with ${error}`);
Z
zengyawen 已提交
472 473 474 475 476
           }
       }
       ```

6. 释放Caller通信接口。
477 478
   Caller不再使用后,应用开发者可以通过release接口释放Caller。

Z
zengyawen 已提交
479 480 481
   ```ts
   releaseCall() {
       try {
zyjhandsome's avatar
zyjhandsome 已提交
482
           this.caller.release();
Z
zengyawen 已提交
483
           this.caller = undefined
zyjhandsome's avatar
zyjhandsome 已提交
484
           console.info('caller release succeed');
Z
zengyawen 已提交
485
       } catch (error) {
zyjhandsome's avatar
zyjhandsome 已提交
486
           console.info(`caller release failed with ${error}`);
Z
zengyawen 已提交
487 488 489
       }
   }
   ```
X
xinking129 已提交
490

491
## 退出由跨设备拉起的serviceextensionability组件
X
xinking129 已提交
492

493
退出由跨设备拉起的ServiceExtensionAbility组件。
X
xinking129 已提交
494

495
下面介绍退出由跨设备拉起的ServiceExtensionAbility组件的方法。
X
xinking129 已提交
496 497 498 499


### 接口说明

X
xinking129 已提交
500
  **表5** API接口功能介绍
X
xinking129 已提交
501 502 503

| 接口名 | 描述 |
| -------- | -------- |
504 505
| stopServiceExtensionAbility(want: Want): Promise<void>; | 退出启动的ServiceExtensionAbility,Promise形式接口。 |
| stopServiceExtensionAbility(want: Want, callback: AsyncCallback<void>): void; | 退出启动的ServiceExtensionAbility,callback形式接口。 |
X
xinking129 已提交
506 507 508 509


### 开发步骤

510
1. 使用stopServiceExtensionAbility接口退出由startAbility接口或connectServiceExtensionAbility接口拉起的跨设备ServiceExtension应用。
X
xinking129 已提交
511 512 513 514 515 516 517 518 519 520 521

     1. 导入接口模块

        ```ts
         import Ability from '@ohos.app.ability.UIAbility';
         ```
        or
        ```ts
         import ServiceExtensionAbility from '@ohos.app.ability.ServiceExtensionAbility';
         ```

522
     2. 退出使用startAbility接口启动的跨设备ServiceExtension应用。
X
xinking129 已提交
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
     
         ```ts
            let deviceId = "device ID obtained from manager"
            let want = {
                deviceId: deviceId,
                bundleName: "com.acts.actsstopserviceextensionmanualtest",
                abilityName: "ServiceAbility",
            };
            this.context.startAbility(want).then(() => {
                console.info("start ability success")
                this.context.stopServiceExtensionAbility(want).then(() => {
                    console.info("stop service extension ability success")
                }).catch((err) => {
                    console.info("stop service extension ability err is " + JSON.stringify(err))
                })
            }).catch((err) => {
                console.info("start ability err is " + JSON.stringify(err))
            })
         ```

543
     3. 退出使用connectServiceExtensionAbility接口启动的跨设备ServiceExtension应用。
X
xinking129 已提交
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
     
         ```ts
            let deviceId = "device ID obtained from manager"
            let want = {
                deviceId: deviceId,
                bundleName: "com.acts.actsstopserviceextensionmanualtest",
                abilityName: "ServiceAbility",
            };
            let context = this.context;
            let connectOptions = {
                onConnect(elementName, remote) {
                    console.info("onConnect called elementName is {" + JSON.stringify(elementName) + "}" + " remote is {" + JSON.stringify(remote) + "}");
                    context.stopServiceExtensionAbility(want).then(() => {
                        console.info("stop service extension ability success")
                    }).catch((err) => {
                        console.info("stop service extension ability err is " + JSON.stringify(err))
                    })
                },
                onDisconnect(elementName) {
                    console.info("onDisconnect called elementName is {" + JSON.stringify(elementName) + "}");
                },
                onFailed(code) {
                    console.info("onFailed called elementName is {" + JSON.stringify(code) + "}");
                }
            }
            this.context.connectServiceExtensionAbility(want, connectOptions)
         ```