0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

鴻蒙Ability Kit(程序框架服務(wù))【ServiceExtensionAbility】

jf_46214456 ? 來源:jf_46214456 ? 作者:jf_46214456 ? 2024-06-04 14:50 ? 次閱讀

ServiceExtensionAbility

概述

[ServiceExtensionAbility]是SERVICE類型的ExtensionAbility組件,提供后臺(tái)服務(wù)能力,其內(nèi)部持有了一個(gè)[ServiceExtensionContext],通過[ServiceExtensionContext]提供了豐富的接口供外部使用。

本文描述中稱被啟動(dòng)的ServiceExtensionAbility為服務(wù)端,稱啟動(dòng)ServiceExtensionAbility的組件為客戶端。

[ServiceExtensionAbility]可以被其他組件啟動(dòng)或連接,并根據(jù)調(diào)用者的請(qǐng)求信息在后臺(tái)處理相關(guān)事務(wù)。[ServiceExtensionAbility]支持以啟動(dòng)和連接兩種形式運(yùn)行,系統(tǒng)應(yīng)用可以調(diào)用[startServiceExtensionAbility()]方法啟動(dòng)后臺(tái)服務(wù),也可以調(diào)用[connectServiceExtensionAbility()]方法連接后臺(tái)服務(wù),而三方應(yīng)用只能調(diào)用[connectServiceExtensionAbility()]方法連接后臺(tái)服務(wù)。啟動(dòng)和連接后臺(tái)服務(wù)的差別:

  • 啟動(dòng) :AbilityA啟動(dòng)ServiceB,啟動(dòng)后AbilityA和ServiceB為弱關(guān)聯(lián),AbilityA退出后,ServiceB可以繼續(xù)存在。
  • 連接 :AbilityA連接ServiceB,連接后AbilityA和ServiceB為強(qiáng)關(guān)聯(lián),AbilityA退出后,ServiceB也一起退出。

此處有如下細(xì)節(jié)需要注意:

  • 若Service只通過connect的方式被拉起,那么該Service的生命周期將受客戶端控制,當(dāng)客戶端調(diào)用一次[connectServiceExtensionAbility()]方法,將建立一個(gè)連接,當(dāng)客戶端退出或者調(diào)用[disconnectServiceExtensionAbility()]方法,該連接將斷開。當(dāng)所有連接都斷開后,Service將自動(dòng)退出。
  • Service一旦通過start的方式被拉起,將不會(huì)自動(dòng)退出,系統(tǒng)應(yīng)用可以調(diào)用[stopServiceExtensionAbility()]方法將Service退出。
  • 只能在主線程線程中執(zhí)行connect/disconnect操作,不要在Worker、TaskPool等子線程中執(zhí)行connect/disconnect操作。

說明:
開發(fā)前請(qǐng)熟悉鴻蒙開發(fā)指導(dǎo)文檔gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md點(diǎn)擊或者復(fù)制轉(zhuǎn)到。

  1. 當(dāng)前不支持三方應(yīng)用實(shí)現(xiàn)ServiceExtensionAbility。如果三方開發(fā)者想要實(shí)現(xiàn)后臺(tái)處理相關(guān)事務(wù)的功能,可以使用后臺(tái)任務(wù),具體請(qǐng)參見[后臺(tái)任務(wù)]。
  2. 三方應(yīng)用的UIAbility組件可以通過Context連接系統(tǒng)提供的ServiceExtensionAbility。
  3. 三方應(yīng)用需要在前臺(tái)獲焦的情況下才能連接系統(tǒng)提供的ServiceExtensionAbility。
    搜狗高速瀏覽器截圖20240326151450.png

生命周期

[ServiceExtensionAbility]提供了onCreate()、onRequest()、onConnect()、onDisconnect()和onDestroy()生命周期回調(diào),根據(jù)需要重寫對(duì)應(yīng)的回調(diào)方法。下圖展示了ServiceExtensionAbility的生命周期。

圖1 ServiceExtensionAbility生命周期
ServiceExtensionAbility-lifecycle

  • onCreate 服務(wù)被首次創(chuàng)建時(shí)觸發(fā)該回調(diào),開發(fā)者可以在此進(jìn)行一些初始化的操作,例如注冊(cè)公共事件監(jiān)聽等。

    說明: 如果服務(wù)已創(chuàng)建,再次啟動(dòng)該ServiceExtensionAbility不會(huì)觸發(fā)onCreate()回調(diào)。

  • onRequest 當(dāng)另一個(gè)組件調(diào)用[startServiceExtensionAbility()]方法啟動(dòng)該服務(wù)組件時(shí),觸發(fā)該回調(diào)。執(zhí)行此方法后,服務(wù)會(huì)啟動(dòng)并在后臺(tái)運(yùn)行。每調(diào)用一次[startServiceExtensionAbility()]方法均會(huì)觸發(fā)該回調(diào)。
  • onConnect 當(dāng)另一個(gè)組件調(diào)用[connectServiceExtensionAbility()]方法與該服務(wù)連接時(shí),觸發(fā)該回調(diào)。開發(fā)者在此方法中,返回一個(gè)遠(yuǎn)端代理對(duì)象(IRemoteObject),客戶端拿到這個(gè)對(duì)象后可以通過這個(gè)對(duì)象與服務(wù)端進(jìn)行RPC通信,同時(shí)系統(tǒng)側(cè)也會(huì)將該遠(yuǎn)端代理對(duì)象(IRemoteObject)儲(chǔ)存。后續(xù)若有組件再調(diào)用[connectServiceExtensionAbility()]方法,系統(tǒng)側(cè)會(huì)直接將所保存的遠(yuǎn)端代理對(duì)象(IRemoteObject)返回,而不再觸發(fā)該回調(diào)。
  • onDisconnect 當(dāng)最后一個(gè)連接斷開時(shí),將觸發(fā)該回調(diào)??蛻舳怂劳龌蛘哒{(diào)用[disconnectServiceExtensionAbility()]方法可以使連接斷開。
  • onDestroy 當(dāng)不再使用服務(wù)且準(zhǔn)備將其銷毀該實(shí)例時(shí),觸發(fā)該回調(diào)。開發(fā)者可以在該回調(diào)中清理資源,如注銷監(jiān)聽等。

實(shí)現(xiàn)一個(gè)后臺(tái)服務(wù)(僅對(duì)系統(tǒng)應(yīng)用開放)

開發(fā)準(zhǔn)備

只有系統(tǒng)應(yīng)用才允許實(shí)現(xiàn)ServiceExtensionAbility,因此開發(fā)者在開發(fā)之前需做如下準(zhǔn)備:

  • 替換Full SDK :ServiceExtensionAbility相關(guān)接口都被標(biāo)記為System-API,默認(rèn)對(duì)開發(fā)者隱藏,因此需要手動(dòng)從鏡像站點(diǎn)獲取Full SDK,并在DevEco Studio中替換,具體操作可參考[替換指南]。
  • 申請(qǐng)AllowAppUsePrivilegeExtension特權(quán) :只有具有AllowAppUsePrivilegeExtension特權(quán)的應(yīng)用才允許開發(fā)ServiceExtensionAbility,具體申請(qǐng)方式可參考[應(yīng)用特權(quán)配置指南])。

定義IDL接口

ServiceExtensionAbility作為后臺(tái)服務(wù),需要向外部提供可調(diào)用的接口,開發(fā)者可將接口定義在idl文件中,并使用[IDL工具]生成對(duì)應(yīng)的proxy、stub文件。此處定義一個(gè)名為IIdlServiceExt.idl的文件作為示例:

interface OHOS.IIdlServiceExt {
  int ProcessData([in] int data);
  void InsertDataToMap([in] String key, [in] int val);
}

在DevEco Studio工程Module對(duì)應(yīng)的ets目錄下手動(dòng)新建名為IdlServiceExt的目錄,將[IDL工具]生成的文件復(fù)制到該目錄下,并創(chuàng)建一個(gè)名為idl_service_ext_impl.ts的文件,作為idl接口的實(shí)現(xiàn):

├── ets
│ ├── IdlServiceExt
│ │   ├── i_idl_service_ext.ts      # 生成文件
│ │   ├── idl_service_ext_proxy.ts  # 生成文件
│ │   ├── idl_service_ext_stub.ts   # 生成文件
│ │   ├── idl_service_ext_impl.ts   # 開發(fā)者自定義文件,對(duì)idl接口的具體實(shí)現(xiàn)
│ └
└

idl_service_ext_impl.ts實(shí)現(xiàn)如下:

import IdlServiceExtStub from './idl_service_ext_stub';
import hilog from '@ohos.hilog';
import type { insertDataToMapCallback } from './i_idl_service_ext';
import type { processDataCallback } from './i_idl_service_ext';

const ERR_OK = 0;
const TAG: string = "[IdlServiceExtImpl]";
const DOMAIN_NUMBER: number = 0xFF00;

// 開發(fā)者需要在這個(gè)類型里對(duì)接口進(jìn)行實(shí)現(xiàn)
export default class ServiceExtImpl extends IdlServiceExtStub {
  processData(data: number, callback: processDataCallback): void {
    // 開發(fā)者自行實(shí)現(xiàn)業(yè)務(wù)邏輯
    hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`);
    callback(ERR_OK, data + 1); // 鑒權(quán)通過,執(zhí)行正常業(yè)務(wù)邏輯
  }

  insertDataToMap(key: string, val: number, callback: insertDataToMapCallback): void {
    // 開發(fā)者自行實(shí)現(xiàn)業(yè)務(wù)邏輯
    hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key}  val: ${val}`);
    callback(ERR_OK);
  }
}

創(chuàng)建ServiceExtensionAbility

在DevEco Studio工程中手動(dòng)新建一個(gè)ServiceExtensionAbility,具體步驟如下:

  1. 在工程Module對(duì)應(yīng)的ets目錄下,右鍵選擇“New > Directory”,新建一個(gè)目錄并命名為ServiceExtAbility。
  2. 在ServiceExtAbility目錄,右鍵選擇“New > ArkTS File”,新建一個(gè)文件并命名為ServiceExtAbility.ets。
    ├── ets
    │ ├── IdlServiceExt
    │ │   ├── i_idl_service_ext.ets      # 生成文件
    │ │   ├── idl_service_ext_proxy.ets  # 生成文件
    │ │   ├── idl_service_ext_stub.ets   # 生成文件
    │ │   ├── idl_service_ext_impl.ets   # 開發(fā)者自定義文件,對(duì)idl接口的具體實(shí)現(xiàn)
    │ ├── ServiceExtAbility
    │ │   ├── ServiceExtAbility.ets
    └
    
  3. 在ServiceExtAbility.ets文件中,增加導(dǎo)入ServiceExtensionAbility的依賴包,自定義類繼承ServiceExtensionAbility并實(shí)現(xiàn)生命周期回調(diào),在onConnect生命周期回調(diào)里,需要將之前定義的ServiceExtImpl對(duì)象返回。
    import hilog from '@ohos.hilog';
    import ServiceExtensionAbility from '@ohos.app.ability.ServiceExtensionAbility';
    import ServiceExtImpl from '../IdlServiceExt/idl_service_ext_impl';
    import type Want from '@ohos.app.ability.Want';
    import type rpc from '@ohos.rpc';
    
    const TAG: string = '[ServiceExtAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    export default class ServiceExtAbility extends ServiceExtensionAbility {
      serviceExtImpl: ServiceExtImpl = new ServiceExtImpl('ExtImpl');
    
      onCreate(want: Want): void {
        let serviceExtensionContext = this.context;
        hilog.info(DOMAIN_NUMBER, TAG, `onCreate, want: ${want.abilityName}`);
      };
    
      onRequest(want: Want, startId: number): void {
        hilog.info(DOMAIN_NUMBER, TAG, `onRequest, want: ${want.abilityName}`);
      };
    
      onConnect(want: Want): rpc.RemoteObject {
        hilog.info(DOMAIN_NUMBER, TAG, `onConnect, want: ${want.abilityName}`);
        // 返回ServiceExtImpl對(duì)象,客戶端獲取后便可以與ServiceExtensionAbility進(jìn)行通信
        return this.serviceExtImpl as rpc.RemoteObject;
      };
    
      onDisconnect(want: Want): void {
        hilog.info(DOMAIN_NUMBER, TAG, `onDisconnect, want: ${want.abilityName}`);
      };
    
      onDestroy(): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onDestroy');
      };
    };
    
  4. 在工程Module對(duì)應(yīng)的[module.json5配置文件]中注冊(cè)ServiceExtensionAbility,type標(biāo)簽需要設(shè)置為“service”,srcEntry標(biāo)簽表示當(dāng)前ExtensionAbility組件所對(duì)應(yīng)的代碼路徑。
    {
      "module": {
        ...
        "extensionAbilities": [
          {
            "name": "ServiceExtAbility",
            "icon": "$media:icon",
            "description": "service",
            "type": "service",
            "exported": true,
            "srcEntry": "./ets/ServiceExtAbility/ServiceExtAbility.ets"
          }
        ]
      }
    }
    

啟動(dòng)一個(gè)后臺(tái)服務(wù)(僅對(duì)系統(tǒng)應(yīng)用開放)

系統(tǒng)應(yīng)用通過[startServiceExtensionAbility()]方法啟動(dòng)一個(gè)后臺(tái)服務(wù),服務(wù)的[onRequest()]回調(diào)就會(huì)被調(diào)用,并在該回調(diào)方法中接收到調(diào)用者傳遞過來的want對(duì)象。后臺(tái)服務(wù)啟動(dòng)后,其生命周期獨(dú)立于客戶端,即使客戶端已經(jīng)銷毀,該后臺(tái)服務(wù)仍可繼續(xù)運(yùn)行。因此,后臺(tái)服務(wù)需要在其工作完成時(shí)通過調(diào)用ServiceExtensionContext的[terminateSelf()]來自行停止,或者由另一個(gè)組件調(diào)用[stopServiceExtensionAbility()]來將其停止。

說明: ServiceExtensionContext的[startServiceExtensionAbility()]、[stopServiceExtensionAbility()]和[terminateSelf()]為系統(tǒng)接口,三方應(yīng)用不支持調(diào)用。

  1. 在系統(tǒng)應(yīng)用中啟動(dòng)一個(gè)新的ServiceExtensionAbility。示例中的context的獲取方式請(qǐng)參見[獲取UIAbility的上下文信息]。
    import common from '@ohos.app.ability.common';
    import Want from '@ohos.app.ability.Want';
    import { BusinessError } from '@ohos.base';
    import promptAction from '@ohos.promptAction';
    import hilog from '@ohos.hilog';
    
    const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    @Entry
    @Component
    struct Page_ServiceExtensionAbility {
      build() {
        Column() {
          //...
          List({ initialIndex: 0 }) {
            ListItem() {
              Row() {
                //...
              }
              .onClick(() = > {
                let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
                let want: Want = {
                  deviceId: '',
                  bundleName: 'com.samples.stagemodelabilitydevelop',
                  abilityName: 'ServiceExtAbility'
                };
                context.startServiceExtensionAbility(want).then(() = > {
                  hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting ServiceExtensionAbility.');
                  // 成功啟動(dòng)后臺(tái)服務(wù)
                  promptAction.showToast({
                    message: $r('app.string.SuccessfullyStartBackendService')
                  });
                }).catch((err: BusinessError) = > {
                  hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`);
                });
              })
            }
            //...
          }
          //...
        }
        //...
      }
    }
    
  2. 在系統(tǒng)應(yīng)用中停止一個(gè)已啟動(dòng)的ServiceExtensionAbility。
    import common from '@ohos.app.ability.common';
    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    import Want from '@ohos.app.ability.Want';
    import { BusinessError } from '@ohos.base';
    
    const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    @Entry
    @Component
    struct Page_ServiceExtensionAbility {
      build() {
        Column() {
          //...
          List({ initialIndex: 0 }) {
            ListItem() {
              Row() {
                //...
              }
              .onClick(() = > {
                let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
                let want: Want = {
                  deviceId: '',
                  bundleName: 'com.samples.stagemodelabilitydevelop',
                  abilityName: 'ServiceExtAbility'
                };
                context.stopServiceExtensionAbility(want).then(() = > {
                  hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in stopping ServiceExtensionAbility.');
                  promptAction.showToast({
                    message: $r('app.string.SuccessfullyStoppedAStartedBackendService')
                  });
                }).catch((err: BusinessError) = > {
                  hilog.error(DOMAIN_NUMBER, TAG, `Failed to stop ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`);
                });
              })
            }
            //...
          }
          //...
        }
        //...
      }
    }
    
  3. 已啟動(dòng)的ServiceExtensionAbility停止自身。
    import common from '@ohos.app.ability.common';
    import { BusinessError } from '@ohos.base';
    import promptAction from '@ohos.promptAction';
    import hilog from '@ohos.hilog';
    import Want from '@ohos.app.ability.Want';
    
    const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    @Entry
    @Component
    struct Page_ServiceExtensionAbility {
      build() {
        Column() {
          //...
          List({ initialIndex: 0 }) {
            ListItem() {
              Row() {
                //...
              }
              .onClick(() = > {
                let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
                context.terminateSelf().then(() = > {
                  hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in terminating self.');
                  // 成功停止當(dāng)前后臺(tái)服務(wù)
                  promptAction.showToast({
                    message: $r('app.string.SuccessfullyStopStartedBackendService')
                  });
                }).catch((err: BusinessError) = > {
                  hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`);
                });
              })
            }
            //...
          }
          //...
        }
        //...
      }
    }
    

說明: 后臺(tái)服務(wù)可以在后臺(tái)長(zhǎng)期運(yùn)行,為了避免資源浪費(fèi),需要對(duì)后臺(tái)服務(wù)的生命周期進(jìn)行管理。即一個(gè)后臺(tái)服務(wù)完成了請(qǐng)求方的任務(wù),需要及時(shí)銷毀。銷毀已啟動(dòng)的后臺(tái)服務(wù)有兩種方式:

  • 后臺(tái)服務(wù)自身調(diào)用[terminateSelf()]方法來自行停止。
  • 由其他組件調(diào)用[stopServiceExtensionAbility()]方法來停止。 調(diào)用[terminateSelf()]或[stopServiceExtensionAbility()]方法之后,系統(tǒng)將銷毀后臺(tái)服務(wù)。

連接一個(gè)后臺(tái)服務(wù)

系統(tǒng)應(yīng)用或者三方應(yīng)用可以通過[connectServiceExtensionAbility()]連接一個(gè)服務(wù)(在Want對(duì)象中指定啟動(dòng)的目標(biāo)服務(wù)),服務(wù)的[onConnect()]就會(huì)被調(diào)用,并在該回調(diào)方法中接收到調(diào)用者傳遞過來的Want對(duì)象,從而建立長(zhǎng)連接。

ServiceExtensionAbility服務(wù)組件在[onConnect()]中返回IRemoteObject對(duì)象,開發(fā)者通過該IRemoteObject定義通信接口,用于客戶端與服務(wù)端進(jìn)行RPC交互。多個(gè)客戶端可以同時(shí)連接到同一個(gè)后臺(tái)服務(wù),客戶端完成與服務(wù)的交互后,客戶端需要通過調(diào)用[disconnectServiceExtensionAbility()]來斷開連接。如果所有連接到某個(gè)后臺(tái)服務(wù)的客戶端均已斷開連接,則系統(tǒng)會(huì)銷毀該服務(wù)。

  • 使用connectServiceExtensionAbility()建立與后臺(tái)服務(wù)的連接。示例中的context的獲取方式請(qǐng)參見[獲取UIAbility的上下文信息]。
    import common from '@ohos.app.ability.common';
    import deviceManager from '@ohos.distributedDeviceManager';
    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    import rpc from '@ohos.rpc';
    import Want from '@ohos.app.ability.Want';
    // 客戶端需要將服務(wù)端對(duì)外提供的idl_service_ext_proxy.ts導(dǎo)入到本地工程中
    import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy';
    
    const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    let connectionId: number;
    let want: Want = {
      deviceId: '',
      bundleName: 'com.samples.stagemodelabilitydevelop',
      abilityName: 'ServiceExtAbility'
    };
    
    let options: common.ConnectOptions = {
      onConnect(elementName, remote: rpc.IRemoteObject): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');
        if (remote === null) {
          hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);
          return;
        }
        let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote);
        // 通過接口調(diào)用的方式進(jìn)行通信,屏蔽了RPC通信的細(xì)節(jié),簡(jiǎn)潔明了
        serviceExtProxy.processData(1, (errorCode: number, retVal: number) = > {
          hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`);
        });
        serviceExtProxy.insertDataToMap('theKey', 1, (errorCode: number) = > {
          hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, errorCode: ${errorCode}`);
        })
      },
      onDisconnect(elementName): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
      },
      onFailed(code: number): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code));
      }
    };
    @Entry
    @Component
    struct Page_ServiceExtensionAbility {
      build() {
        Column() {
          //...
          List({ initialIndex: 0 }) {
            ListItem() {
              Row() {
                //...
              }
              .onClick(() = > {
                let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
                // 建立連接后返回的Id需要保存下來,在解綁服務(wù)時(shí)需要作為參數(shù)傳入
                connectionId = context.connectServiceExtensionAbility(want, options);
                // 成功連接后臺(tái)服務(wù)
                promptAction.showToast({
                  message: $r('app.string.SuccessfullyConnectBackendService')
                });
                // connectionId = context.connectAbility(want, options);
                hilog.info(DOMAIN_NUMBER, TAG, `connectionId is : ${connectionId}`);
              })
            }
            //...
          }
          //...
        }
        //...
      }
    }
    
  • 使用disconnectServiceExtensionAbility()斷開與后臺(tái)服務(wù)的連接。
    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    import common from '@ohos.app.ability.common';
    import { BusinessError } from '@ohos.base';
    
    const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    let connectionId: number;
    @Entry
    @Component
    struct Page_ServiceExtensionAbility {
      build() {
        Column() {
          //...
          List({ initialIndex: 0 }) {
            ListItem() {
              Row() {
                //...
              }
              .onClick(() = > {
                let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
                // connectionId為調(diào)用connectServiceExtensionAbility接口時(shí)的返回值,需開發(fā)者自行維護(hù)
                context.disconnectServiceExtensionAbility(connectionId).then(() = > {
                  hilog.info(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility success');
                  // 成功斷連后臺(tái)服務(wù)
                  promptAction.showToast({
                    message: $r('app.string.SuccessfullyDisconnectBackendService')
                  });
                }).catch((error: BusinessError) = > {
                  hilog.error(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility failed');
                });
              })
            }
            //...
          }
          //...
        }
        //...
      }
    }
    

客戶端與服務(wù)端通信

客戶端在onConnect()中獲取到[rpc.RemoteObject]對(duì)象后便可與Service進(jìn)行通信,有如下兩種方式:

  • 使用服務(wù)端提供的IDL接口進(jìn)行通信(推薦
    // 客戶端需要將服務(wù)端對(duì)外提供的idl_service_ext_proxy.ts導(dǎo)入到本地工程中
    import common from '@ohos.app.ability.common';
    import hilog from '@ohos.hilog';
    import rpc from '@ohos.rpc';
    import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy';
    
    const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    let options: common.ConnectOptions = {
      onConnect(elementName, remote: rpc.IRemoteObject): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');
        if (remote === null) {
          hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);
          return;
        }
        let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote);
        // 通過接口調(diào)用的方式進(jìn)行通信,屏蔽了RPC通信的細(xì)節(jié),簡(jiǎn)潔明了
        serviceExtProxy.processData(1, (errorCode: number, retVal: number) = > {
          hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`);
        });
        serviceExtProxy.insertDataToMap('theKey', 1, (errorCode: number) = > {
          hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, errorCode: ${errorCode}`);
        })
      },
      onDisconnect(elementName): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
      },
      onFailed(code: number): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code));
      }
    };
    
  • 直接使用[sendMessageRequest]接口向服務(wù)端發(fā)送消息(不推薦)
    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    import rpc from '@ohos.rpc';
    import common from '@ohos.app.ability.common';
    import { BusinessError } from '@ohos.base';
    
    const TAG: string = '[Page_CollaborateAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    const REQUEST_CODE = 1;
    let options: common.ConnectOptions = {
      onConnect(elementName, remote): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');
        if (remote === null) {
          hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);
          return;
        }
        let option = new rpc.MessageOption();
        let data = new rpc.MessageSequence();
        let reply = new rpc.MessageSequence();
    
        data.writeInt(99);
        // 開發(fā)者可發(fā)送data到目標(biāo)端應(yīng)用進(jìn)行相應(yīng)操作
        // @param code 表示客戶端發(fā)送的服務(wù)請(qǐng)求代碼。
        // @param data 表示客戶端發(fā)送的{@link MessageSequence}對(duì)象。
        // @param reply 表示遠(yuǎn)程服務(wù)發(fā)送的響應(yīng)消息對(duì)象。
        // @param options 指示操作是同步的還是異步的。
        // @return 如果操作成功返回{@code true}; 否則返回 {@code false}。
    
        remote.sendMessageRequest(REQUEST_CODE, data, reply, option).then((ret: rpc.RequestResult) = > {
          let errCode = reply.readInt(); // 在成功連接的情況下,會(huì)收到來自目標(biāo)端返回的信息(100)
          let msg: number = 0;
          if (errCode === 0) {
            msg = reply.readInt();
          }
          hilog.info(DOMAIN_NUMBER, TAG, `sendRequest msg:${msg}`);
          // 成功連接后臺(tái)服務(wù)
          promptAction.showToast({
            message: `sendRequest msg:${msg}`
          });
        }).catch((error: BusinessError) = > {
          hilog.info(DOMAIN_NUMBER, TAG, `sendRequest failed, ${JSON.stringify(error)}`);
        });
      },
      onDisconnect(elementName): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
      },
      onFailed(code): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback');
      }
    };
    //...
    

服務(wù)端對(duì)客戶端身份校驗(yàn)

部分開發(fā)者需要使用ServiceExtension提供一些較為敏感的服務(wù),因此需要對(duì)客戶端身份進(jìn)行校驗(yàn),開發(fā)者可在IDL接口的stub端進(jìn)行校驗(yàn),IDL接口實(shí)現(xiàn)詳見上文[定義IDL接口],此處推薦兩種校驗(yàn)方式:

  • 通過callerUid識(shí)別客戶端應(yīng)用
    通過調(diào)用[getCallingUid()]接口獲取客戶端的uid,再調(diào)用[getBundleNameByUid()]接口獲取uid對(duì)應(yīng)的bundleName,從而識(shí)別客戶端身份。此處需要注意的是[getBundleNameByUid()]是一個(gè)異步接口,因此服務(wù)端無法將校驗(yàn)結(jié)果返回給客戶端,這種校驗(yàn)方式適合客戶端向服務(wù)端發(fā)起執(zhí)行異步任務(wù)請(qǐng)求的場(chǎng)景,示例代碼如下:
    import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
    import bundleManager from '@ohos.bundle.bundleManager';
    import IdlServiceExtStub from './idl_service_ext_stub';
    import hilog from '@ohos.hilog';
    import rpc from '@ohos.rpc';
    import type { BusinessError } from '@ohos.base';
    import type { InsertDataToMapCallback } from './i_idl_service_ext';
    import type { ProcessDataCallback } from './i_idl_service_ext';
    
    const ERR_OK = 0;
    const ERR_DENY = -1;
    const TAG: string = "[IdlServiceExtImpl]";
    const DOMAIN_NUMBER: number = 0xFF00;
    
    // 開發(fā)者需要在這個(gè)類型里對(duì)接口進(jìn)行實(shí)現(xiàn)
    export default class ServiceExtImpl extends IdlServiceExtStub {
      processData(data: number, callback: ProcessDataCallback): void {
        // 開發(fā)者自行實(shí)現(xiàn)業(yè)務(wù)邏輯
        hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`);
        let callerUid = rpc.IPCSkeleton.getCallingUid();
        bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) = > {
          hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName);
          // 對(duì)客戶端包名進(jìn)行識(shí)別
          if (callerBundleName !== 'com.samples.stagemodelabilitydevelop') { // 識(shí)別不通過
            hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject');
            return;
          }
          // 識(shí)別通過,執(zhí)行正常業(yè)務(wù)邏輯
        }).catch((err: BusinessError) = > {
          hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message);
        });
        //...
      };
    
      insertDataToMap(key: string, val: number, callback: InsertDataToMapCallback): void {
        // 開發(fā)者自行實(shí)現(xiàn)業(yè)務(wù)邏輯
        hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key}  val: ${val}`);
        callback(ERR_OK);
      };
    };
    
  • 通過callerTokenId對(duì)客戶端進(jìn)行鑒權(quán)
    通過調(diào)用[getCallingTokenId()]接口獲取客戶端的tokenID,再調(diào)用[verifyAccessTokenSync()]接口判斷客戶端是否有某個(gè)具體權(quán)限,由于當(dāng)前不支持自定義權(quán)限,因此只能校驗(yàn)當(dāng)前[系統(tǒng)所定義的權(quán)限]。示例代碼如下:
    import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
    import bundleManager from '@ohos.bundle.bundleManager';
    import IdlServiceExtStub from './idl_service_ext_stub';
    import hilog from '@ohos.hilog';
    import rpc from '@ohos.rpc';
    import type { BusinessError } from '@ohos.base';
    import type { InsertDataToMapCallback } from './i_idl_service_ext';
    import type { ProcessDataCallback } from './i_idl_service_ext';
    
    const ERR_OK = 0;
    const ERR_DENY = -1;
    const TAG: string = '[IdlServiceExtImpl]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    // 開發(fā)者需要在這個(gè)類型里對(duì)接口進(jìn)行實(shí)現(xiàn)
    export default class ServiceExtImpl extends IdlServiceExtStub {
      processData(data: number, callback: ProcessDataCallback): void {
        // 開發(fā)者自行實(shí)現(xiàn)業(yè)務(wù)邏輯
        hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`);
    
        let callerUid = rpc.IPCSkeleton.getCallingUid();
        bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) = > {
          hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName);
          // 對(duì)客戶端包名進(jìn)行識(shí)別
          if (callerBundleName !== 'com.samples.stagemodelabilitydevelop') { // 識(shí)別不通過
            hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject');
            return;
          }
          // 識(shí)別通過,執(zhí)行正常業(yè)務(wù)邏輯
        }).catch((err: BusinessError) = > {
          hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message);
        });
    
        let callerTokenId = rpc.IPCSkeleton.getCallingTokenId();
        let accessManger = abilityAccessCtrl.createAtManager();
        // 所校驗(yàn)的具體權(quán)限由開發(fā)者自行選擇,此處ohos.permission.GET_BUNDLE_INFO_PRIVILEGED只作為示例
        let grantStatus = accessManger.verifyAccessTokenSync(callerTokenId, 'ohos.permission.GET_BUNDLE_INFO_PRIVILEGED');
        if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) {
          hilog.info(DOMAIN_NUMBER, TAG, 'PERMISSION_DENIED');
          callback(ERR_DENY, data); // 鑒權(quán)失敗,返回錯(cuò)誤
          return;
        }
        hilog.info(DOMAIN_NUMBER, TAG, 'verify access token success.');
        callback(ERR_OK, data + 1); // 鑒權(quán)通過,執(zhí)行正常業(yè)務(wù)邏輯
      };
    
      insertDataToMap(key: string, val: number, callback: InsertDataToMapCallback): void {
        // 開發(fā)者自行實(shí)現(xiàn)業(yè)務(wù)邏輯
        hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key}  val: ${val}`);
        callback(ERR_OK);
      };
    };
    

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 框架
    +關(guān)注

    關(guān)注

    0

    文章

    398

    瀏覽量

    17405
  • 程序
    +關(guān)注

    關(guān)注

    116

    文章

    3762

    瀏覽量

    80754
  • 鴻蒙
    +關(guān)注

    關(guān)注

    57

    文章

    2302

    瀏覽量

    42689
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    鴻蒙開發(fā)接口Ability框架:【@ohos.application.Ability (Ability)】

    Ability模塊提供對(duì)Ability生命周期、上下文環(huán)境等調(diào)用管理的能力,包括Ability創(chuàng)建、銷毀、轉(zhuǎn)儲(chǔ)客戶端信息等。
    的頭像 發(fā)表于 04-30 17:42 ?2184次閱讀
    <b class='flag-5'>鴻蒙</b>開發(fā)接口<b class='flag-5'>Ability</b><b class='flag-5'>框架</b>:【@ohos.application.<b class='flag-5'>Ability</b> (<b class='flag-5'>Ability</b>)】

    鴻蒙開發(fā)接口Ability框架:【@ohos.ability.featureAbility (FeatureAbility模塊)】

    FeatureAbility模塊提供帶有UI設(shè)計(jì)與用戶交互的能力,包括啟動(dòng)新的ability、獲取dataAbilityHelper、設(shè)置此Page Ability、獲取當(dāng)前Ability對(duì)應(yīng)的窗口,連接
    的頭像 發(fā)表于 05-06 16:31 ?905次閱讀
    <b class='flag-5'>鴻蒙</b>開發(fā)接口<b class='flag-5'>Ability</b><b class='flag-5'>框架</b>:【@ohos.<b class='flag-5'>ability</b>.featureAbility (FeatureAbility模塊)】

    鴻蒙開發(fā)接口Ability框架:【 (ServiceExtensionAbility)】

    ServiceExtensionAbility模塊提供ServiceExtension服務(wù)擴(kuò)展相關(guān)接口的能力。
    的頭像 發(fā)表于 05-09 09:59 ?756次閱讀
    <b class='flag-5'>鴻蒙</b>開發(fā)接口<b class='flag-5'>Ability</b><b class='flag-5'>框架</b>:【 (<b class='flag-5'>ServiceExtensionAbility</b>)】

    鴻蒙開發(fā)接口Ability框架:【 (Context模塊)】

    Context模塊提供了ability或application的上下文的能力,包括允許訪問特定于應(yīng)用程序的資源、請(qǐng)求和驗(yàn)證權(quán)限等。
    的頭像 發(fā)表于 05-13 16:04 ?654次閱讀
    <b class='flag-5'>鴻蒙</b>開發(fā)接口<b class='flag-5'>Ability</b><b class='flag-5'>框架</b>:【 (Context模塊)】

    鴻蒙Ability Kit程序框架服務(wù))【UIExtensionAbility】

    [UIExtensionAbility]是UI類型的ExtensionAbility組件,需要與[UIExtensionComponent]一起配合使用,開發(fā)者可以在UIAbility的頁面中通過UIExtensionComponent嵌入提供方應(yīng)用的UIExtensionAbility提供的UI。UIExtensionAbility會(huì)在獨(dú)立于UIAbility的進(jìn)程中運(yùn)行,完成其頁面的布局和渲染。常用于有進(jìn)程隔離訴求的系統(tǒng)彈窗、狀態(tài)欄、膠囊等模塊化開發(fā)的場(chǎng)景。
    的頭像 發(fā)表于 06-05 09:19 ?1257次閱讀
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b>(<b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服務(wù)</b>)【UIExtensionAbility】

    HarmonyOS NEXT Developer Beta1中的Kit

    、AI六大領(lǐng)域,例如: 應(yīng)用框架相關(guān)Kit開放能力:Ability Kit程序框架
    發(fā)表于 06-26 10:47

    鴻蒙應(yīng)用模型:【Ability Kit】簡(jiǎn)介

    Ability Kit程序框架服務(wù))提供了應(yīng)用程序開發(fā)和運(yùn)行的應(yīng)用模型,是系統(tǒng)為開發(fā)者提供的應(yīng)
    的頭像 發(fā)表于 05-29 14:41 ?557次閱讀
    <b class='flag-5'>鴻蒙</b>應(yīng)用模型:【<b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b>】簡(jiǎn)介

    鴻蒙Ability Kit程序框架服務(wù))【Ability內(nèi)頁面間的跳轉(zhuǎn)】

    基于Stage模型下的Ability開發(fā),實(shí)現(xiàn)Ability內(nèi)頁面間的跳轉(zhuǎn)和數(shù)據(jù)傳遞。
    的頭像 發(fā)表于 06-03 20:43 ?257次閱讀
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b>(<b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服務(wù)</b>)【<b class='flag-5'>Ability</b>內(nèi)頁面間的跳轉(zhuǎn)】

    鴻蒙Ability Kit程序框架服務(wù))【ExtensionAbility組件】

    ExtensionAbility組件是基于特定場(chǎng)景(例如服務(wù)卡片、輸入法等)提供的應(yīng)用組件,以便滿足更多的使用場(chǎng)景。
    的頭像 發(fā)表于 06-04 15:54 ?462次閱讀
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b>(<b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服務(wù)</b>)【ExtensionAbility組件】

    鴻蒙Ability Kit程序框架服務(wù))【AbilityServiceExtensionAbility通信】

    本示例展示通過[IDL的方式]和?[@ohos.rpc]?等接口實(shí)現(xiàn)了AbilityServiceExtensionAbility之間的通信。
    的頭像 發(fā)表于 06-05 09:28 ?435次閱讀
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b>(<b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服務(wù)</b>)【<b class='flag-5'>Ability</b>與<b class='flag-5'>ServiceExtensionAbility</b>通信】

    鴻蒙Ability Kit程序框架服務(wù))【組件啟動(dòng)規(guī)則(Stage模型)】

    組件啟動(dòng)規(guī)則(Stage模型) 啟動(dòng)組件是指一切啟動(dòng)或連接應(yīng)用組件的行為: 啟動(dòng)UIAbility、ServiceExtensionAbility、DataShareExtensionAbility
    的頭像 發(fā)表于 06-10 18:47 ?775次閱讀
    <b class='flag-5'>鴻蒙</b><b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b>(<b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服務(wù)</b>)【組件啟動(dòng)規(guī)則(Stage模型)】

    鴻蒙Ability Kit程序框架服務(wù))【應(yīng)用啟動(dòng)框架AppStartup】

    `AppStartup`提供了一種更加簡(jiǎn)單高效的初始化組件的方式,支持異步初始化組件加速應(yīng)用的啟動(dòng)時(shí)間。使用啟動(dòng)框架應(yīng)用開發(fā)者只需要分別為待初始化的組件實(shí)現(xiàn)`AppStartup`提供
    的頭像 發(fā)表于 06-10 18:38 ?652次閱讀

    鴻蒙開發(fā)Ability Kit程序框架服務(wù):任務(wù)管理

    AbilityRecord:系統(tǒng)服務(wù)側(cè)管理一個(gè)UIAbility實(shí)例的最小單元,對(duì)應(yīng)一個(gè)應(yīng)用側(cè)的UIAbility組件實(shí)例。系統(tǒng)服務(wù)側(cè)管理UIAbility實(shí)例數(shù)量上限為512個(gè)。
    的頭像 發(fā)表于 06-24 14:46 ?377次閱讀
    <b class='flag-5'>鴻蒙</b>開發(fā)<b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b><b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服務(wù)</b>:任務(wù)管理

    鴻蒙開發(fā)Ability Kit程序框架服務(wù):FA模型綁定Stage模型ServiceExtensionAbility

    本文介紹FA模型的三種應(yīng)用組件如何綁定Stage模型的ServiceExtensionAbility組件。
    的頭像 發(fā)表于 06-25 10:43 ?244次閱讀
    <b class='flag-5'>鴻蒙</b>開發(fā)<b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b><b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服務(wù)</b>:FA模型綁定Stage模型<b class='flag-5'>ServiceExtensionAbility</b>

    鴻蒙開發(fā)Ability Kit程序框架服務(wù):ServiceAbility切換 組件切換

    FA模型中的ServiceAbility對(duì)應(yīng)Stage模型中的ServiceExtensionAbility。Stage模型下的ServiceExtensionAbility為系統(tǒng)API,只有系統(tǒng)
    的頭像 發(fā)表于 06-28 10:24 ?268次閱讀
    <b class='flag-5'>鴻蒙</b>開發(fā)<b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b><b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服務(wù)</b>:ServiceAbility切換 組件切換