多端協(xié)同
功能描述
多端協(xié)同主要包括如下場(chǎng)景:
- [通過跨設(shè)備啟動(dòng)UIAbility和ServiceExtensionAbility組件實(shí)現(xiàn)多端協(xié)同(無返回?cái)?shù)據(jù))]
- [通過跨設(shè)備啟動(dòng)UIAbility組件實(shí)現(xiàn)多端協(xié)同(獲取返回?cái)?shù)據(jù))]
- [通過跨設(shè)備連接ServiceExtensionAbility組件實(shí)現(xiàn)多端協(xié)同]
- [通過跨設(shè)備Call調(diào)用實(shí)現(xiàn)多端協(xié)同]
多端協(xié)同流程
多端協(xié)同流程如下圖所示。
圖1 多端協(xié)同流程圖
約束限制
- 由于“多端協(xié)同任務(wù)管理”能力尚未具備,開發(fā)者當(dāng)前只能通過開發(fā)系統(tǒng)應(yīng)用獲取設(shè)備列表,不支持三方應(yīng)用接入。
- 多端協(xié)同需遵循[分布式跨設(shè)備組件啟動(dòng)規(guī)則]。
- 為了獲得最佳體驗(yàn),使用want傳輸?shù)臄?shù)據(jù)建議在100KB以下。
- 開發(fā)前請(qǐng)熟悉鴻蒙開發(fā)指導(dǎo)文檔 :[
gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md
]
通過跨設(shè)備啟動(dòng)UIAbility和ServiceExtensionAbility組件實(shí)現(xiàn)多端協(xié)同(無返回?cái)?shù)據(jù))
在設(shè)備A上通過發(fā)起端應(yīng)用提供的啟動(dòng)按鈕,啟動(dòng)設(shè)備B上指定的UIAbility與ServiceExtensionAbility。
接口說明
表1 跨設(shè)備啟動(dòng)API接口功能介紹
接口名 | 描述 |
---|---|
startAbility(want: Want, callback: AsyncCallback): void; | 啟動(dòng)UIAbility和ServiceExtensionAbility(callback形式)。 |
stopServiceExtensionAbility(want: Want, callback: AsyncCallback): void; | 退出啟動(dòng)的ServiceExtensionAbility(callback形式)。 |
stopServiceExtensionAbility(want: Want): Promise; | 退出啟動(dòng)的ServiceExtensionAbility(Promise形式)。 |
開發(fā)步驟
- 需要申請(qǐng)
ohos.permission.DISTRIBUTED_DATASYNC
權(quán)限,配置方式請(qǐng)參見[聲明權(quán)限]。 - 同時(shí)需要在應(yīng)用首次啟動(dòng)時(shí)彈窗向用戶申請(qǐng)授權(quán),使用方式請(qǐng)參見[向用戶申請(qǐng)授權(quán)]。
- 獲取目標(biāo)設(shè)備的設(shè)備ID。
import hilog from '@ohos.hilog'; import distributedDeviceManager from '@ohos.distributedDeviceManager'; const TAG: string = '[Page_CollaborateAbility]'; const DOMAIN_NUMBER: number = 0xFF00; let dmClass: distributedDeviceManager.DeviceManager; function initDmClass(): void { // 其中createDeviceManager接口為系統(tǒng)API try { dmClass = distributedDeviceManager.createDeviceManager('com.samples.stagemodelabilitydevelop'); hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass) ?? ''); } catch (err) { hilog.error(DOMAIN_NUMBER, TAG, 'createDeviceManager err: ' + JSON.stringify(err)); }; } function getRemoteDeviceId(): string | undefined { if (typeof dmClass === 'object' && dmClass !== null) { let list = dmClass.getAvailableDeviceListSync(); hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list)); if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null'); return; } if (list.length === 0) { hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`); return; } return list[0].networkId; } else { hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null'); return; } }
- 設(shè)置目標(biāo)組件參數(shù),調(diào)用[
startAbility()
]接口,啟動(dòng)UIAbility或ServiceExtensionAbility。import { BusinessError } from '@ohos.base'; import hilog from '@ohos.hilog'; import Want from '@ohos.app.ability.Want'; import common from '@ohos.app.ability.common'; import distributedDeviceManager from '@ohos.distributedDeviceManager'; import promptAction from '@ohos.promptAction'; const TAG: string = '[Page_CollaborateAbility]'; const DOMAIN_NUMBER: number = 0xFF00; let dmClass: distributedDeviceManager.DeviceManager; function getRemoteDeviceId(): string | undefined { if (typeof dmClass === 'object' && dmClass !== null) { let list = dmClass.getAvailableDeviceListSync(); hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list)); if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null'); return; } if (list.length === 0) { hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`); return; } return list[0].networkId; } else { hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null'); return; } }; @Entry @Component struct Page_CollaborateAbility { private context = getContext(this) as common.UIAbilityContext; build() { Column() { //... List({ initialIndex: 0 }) { //... ListItem() { Row() { //... } .onClick(() = > { let want: Want = { deviceId: getRemoteDeviceId(), bundleName: 'com.samples.stagemodelabilityinteraction', abilityName: 'CollaborateAbility', moduleName: 'entry' // moduleName非必選 }; // context為發(fā)起端UIAbility的AbilityContext this.context.startAbility(want).then(() = > { promptAction.showToast({ message: $r('app.string.SuccessfulCollaboration') }); }).catch((err: BusinessError) = > { hilog.error(DOMAIN_NUMBER, TAG, `startAbility err: ` + JSON.stringify(err)); }); }) } //... } //... } //... } }
- 當(dāng)設(shè)備A發(fā)起端應(yīng)用不需要設(shè)備B上的ServiceExtensionAbility時(shí),可調(diào)用[stopServiceExtensionAbility]接口退出。(該接口不支持UIAbility的退出,UIAbility由用戶手動(dòng)通過任務(wù)管理退出)
import { BusinessError } from '@ohos.base'; import hilog from '@ohos.hilog'; import Want from '@ohos.app.ability.Want'; import common from '@ohos.app.ability.common'; import distributedDeviceManager from '@ohos.distributedDeviceManager'; import promptAction from '@ohos.promptAction'; const TAG: string = '[Page_CollaborateAbility]'; const DOMAIN_NUMBER: number = 0xFF00; let dmClass: distributedDeviceManager.DeviceManager; function getRemoteDeviceId(): string | undefined { if (typeof dmClass === 'object' && dmClass !== null) { let list = dmClass.getAvailableDeviceListSync(); hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list)); if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null'); return; } if (list.length === 0) { hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`); return; } return list[0].networkId; } else { hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null'); return; } }; @Entry @Component struct Page_CollaborateAbility { private context = getContext(this) as common.UIAbilityContext; build() { // ... Button('stopServiceExtensionAbility') .onClick(() = > { let want: Want = { deviceId: getRemoteDeviceId(), bundleName: 'com.example.myapplication', abilityName: 'FuncAbility', moduleName: 'module1', // moduleName非必選 } // 退出由startAbility接口啟動(dòng)的ServiceExtensionAbility this.context.stopServiceExtensionAbility(want).then(() = > { hilog.info(DOMAIN_NUMBER, TAG, "stop service extension ability success") }).catch((err: BusinessError) = > { hilog.error(DOMAIN_NUMBER, TAG, `stop service extension ability err is ` + JSON.stringify(err)); }) }) } }
通過跨設(shè)備啟動(dòng)UIAbility組件實(shí)現(xiàn)多端協(xié)同(獲取返回?cái)?shù)據(jù))
在設(shè)備A上通過應(yīng)用提供的啟動(dòng)按鈕,啟動(dòng)設(shè)備B上指定的UIAbility,當(dāng)設(shè)備B上的UIAbility退出后,會(huì)將返回值發(fā)回設(shè)備A上的發(fā)起端應(yīng)用。
接口說明
表2 跨設(shè)備啟動(dòng),返回結(jié)果數(shù)據(jù)API接口功能描述
接口名 | 描述 |
---|---|
startAbilityForResult(want: Want, callback: AsyncCallback): void; | 啟動(dòng)UIAbility并在該Ability退出的時(shí)候返回執(zhí)行結(jié)果(callback形式)。 |
terminateSelfWithResult(parameter: AbilityResult, callback: AsyncCallback): void; | 停止UIAbility,配合startAbilityForResult使用,返回給接口調(diào)用方AbilityResult信息(callback形式)。 |
terminateSelfWithResult(parameter: AbilityResult): Promise; | 停止UIAbility,配合startAbilityForResult使用,返回給接口調(diào)用方AbilityResult信息(promise形式)。 |
開發(fā)步驟
- 需要申請(qǐng)
ohos.permission.DISTRIBUTED_DATASYNC
權(quán)限,配置方式請(qǐng)參見[聲明權(quán)限]。 - 同時(shí)需要在應(yīng)用首次啟動(dòng)時(shí)彈窗向用戶申請(qǐng)授權(quán),使用方式請(qǐng)參見[向用戶申請(qǐng)授權(quán)]。
- 在發(fā)起端設(shè)置目標(biāo)組件參數(shù),調(diào)用startAbilityForResult()接口啟動(dòng)目標(biāo)端UIAbility,異步回調(diào)中的data用于接收目標(biāo)端UIAbility停止自身后返回給調(diào)用方UIAbility的信息。getRemoteDeviceId方法參照[通過跨設(shè)備啟動(dòng)uiability和serviceextensionability組件實(shí)現(xiàn)多端協(xié)同無返回?cái)?shù)據(jù)]。
import { BusinessError } from '@ohos.base'; import hilog from '@ohos.hilog'; import Want from '@ohos.app.ability.Want'; import common from '@ohos.app.ability.common'; import distributedDeviceManager from '@ohos.distributedDeviceManager'; import promptAction from '@ohos.promptAction'; const DOMAIN_NUMBER: number = 0xFF00; const TAG: string = '[Page_CollaborateAbility]'; let dmClass: distributedDeviceManager.DeviceManager; function getRemoteDeviceId(): string | undefined { if (typeof dmClass === 'object' && dmClass !== null) { let list = dmClass.getAvailableDeviceListSync(); hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list)); if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null'); return; } if (list.length === 0) { hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`); return; } return list[0].networkId; } else { hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null'); return; } }; @Entry @Component struct Page_CollaborateAbility { private context = getContext(this) as common.UIAbilityContext; build() { Column() { //... List({ initialIndex: 0 }) { //... ListItem() { Row() { //... } .onClick(() = > { let want: Want = { deviceId: getRemoteDeviceId(), bundleName: 'com.samples.stagemodelabilityinteraction', abilityName: 'ServiceExtAbility', moduleName: 'entry' // moduleName非必選 }; // 退出由startAbility接口啟動(dòng)的ServiceExtensionAbility this.context.stopServiceExtensionAbility(want).then(() = > { hilog.info(DOMAIN_NUMBER, TAG, 'stop service extension ability success') promptAction.showToast({ message: $r('app.string.SuccessfullyStop') }); }).catch((err: BusinessError) = > { hilog.error(DOMAIN_NUMBER, TAG, `stop service extension ability err is ` + JSON.stringify(err)); }); }) } //... } //... } //... } }
- 在目標(biāo)端UIAbility任務(wù)完成后,調(diào)用terminateSelfWithResult()方法,將數(shù)據(jù)返回給發(fā)起端的UIAbility。
import common from '@ohos.app.ability.common'; import hilog from '@ohos.hilog'; import { BusinessError } from '@ohos.base'; const TAG: string = '[Page_CollaborateAbility]'; const DOMAIN_NUMBER: number = 0xFF00; @Entry @Component struct Page_CollaborateAbility { private context = getContext(this) as common.UIAbilityContext; build() { Column() { //... List({ initialIndex: 0 }) { //... ListItem() { Row() { //... } .onClick(() = > { const RESULT_CODE: number = 1001; // context為目標(biāo)端UIAbility的AbilityContext this.context.terminateSelfWithResult( { resultCode: RESULT_CODE, want: { bundleName: 'ohos.samples.stagemodelabilitydevelop', abilityName: 'CollaborateAbility', moduleName: 'entry', parameters: { info: '來自Page_CollaborateAbility頁面' } } }, (err: BusinessError) = > { hilog.info(DOMAIN_NUMBER, TAG, `terminateSelfWithResult err: ` + JSON.stringify(err)); }); }) } //... } //... } //... } }
- 發(fā)起端UIAbility接收到目標(biāo)端UIAbility返回的信息,對(duì)其進(jìn)行處理。
import { BusinessError } from '@ohos.base'; import hilog from '@ohos.hilog'; import Want from '@ohos.app.ability.Want'; import common from '@ohos.app.ability.common'; import distributedDeviceManager from '@ohos.distributedDeviceManager'; import promptAction from '@ohos.promptAction'; const TAG: string = '[Page_CollaborateAbility]'; const DOMAIN_NUMBER: number = 0xFF00; let dmClass: distributedDeviceManager.DeviceManager; function getRemoteDeviceId(): string | undefined { if (typeof dmClass === 'object' && dmClass !== null) { let list = dmClass.getAvailableDeviceListSync(); hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list)); if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null'); return; } if (list.length === 0) { hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`); return; } return list[0].networkId; } else { hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null'); return; } }; @Entry @Component struct Page_CollaborateAbility { private context = getContext(this) as common.UIAbilityContext; build() { Column() { //... List({ initialIndex: 0 }) { //... ListItem() { Row() { //... } .onClick(() = > { let want: Want = { deviceId: getRemoteDeviceId(), bundleName: 'com.samples.stagemodelabilityinteraction', abilityName: 'CollaborateAbility', moduleName: 'entry' // moduleName非必選 }; const RESULT_CODE: number = 1001; // context為調(diào)用方UIAbility的UIAbilityContext this.context.startAbilityForResult(want).then((data) = > { if (data?.resultCode === RESULT_CODE) { // 解析目標(biāo)端UIAbility返回的信息 let info = data.want?.parameters?.info; hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(info) ?? ''); if (info !== null) { promptAction.showToast({ message : JSON.stringify(info) }); } } }).catch((error: BusinessError) = > { hilog.error(DOMAIN_NUMBER, TAG, `startAbilityForResult err: ` + JSON.stringify(error)); }); }) } //... } //... } //... } }
通過跨設(shè)備連接ServiceExtensionAbility組件實(shí)現(xiàn)多端協(xié)同
系統(tǒng)應(yīng)用可以通過[connectServiceExtensionAbility()]跨設(shè)備連接一個(gè)服務(wù),實(shí)現(xiàn)跨設(shè)備遠(yuǎn)程調(diào)用。比如:分布式游戲場(chǎng)景,平板作為遙控器,智慧屏作為顯示器。
接口說明
表3 跨設(shè)備連接API接口功能介紹
接口名 | 描述 |
---|---|
connectServiceExtensionAbility(want: Want, options: ConnectOptions): number; | 連接ServiceExtensionAbility。 |
disconnectServiceExtensionAbility(connection: number, callback:AsyncCallback): void; | 斷開連接(callback形式)。 |
disconnectServiceExtensionAbility(connection: number): Promise; | 斷開連接(promise形式)。 |
開發(fā)步驟
- 需要申請(qǐng)
ohos.permission.DISTRIBUTED_DATASYNC
權(quán)限,配置方式請(qǐng)參見[聲明權(quán)限]。 - 同時(shí)需要在應(yīng)用首次啟動(dòng)時(shí)彈窗向用戶申請(qǐng)授權(quán),使用方式請(qǐng)參見[向用戶申請(qǐng)授權(quán)]。
- 如果已有后臺(tái)服務(wù),請(qǐng)直接進(jìn)入下一步;如果沒有,則[實(shí)現(xiàn)一個(gè)后臺(tái)服務(wù)(僅對(duì)系統(tǒng)應(yīng)用開放)]。
- 連接一個(gè)后臺(tái)服務(wù)。
實(shí)現(xiàn)IAbilityConnection接口。IAbilityConnection提供了以下方法供開發(fā)者實(shí)現(xiàn):onConnect()是用來處理連接Service成功的回調(diào),onDisconnect()是用來處理Service異常終止的回調(diào),onFailed()是用來處理連接Service失敗的回調(diào)。
設(shè)置目標(biāo)組件參數(shù),包括目標(biāo)設(shè)備ID、Bundle名稱、Ability名稱。
調(diào)用connectServiceExtensionAbility發(fā)起連接。
連接成功,收到目標(biāo)設(shè)備返回的服務(wù)句柄。
進(jìn)行跨設(shè)備調(diào)用,獲得目標(biāo)端服務(wù)返回的結(jié)果。
import { BusinessError } from '@ohos.base'; import hilog from '@ohos.hilog'; import Want from '@ohos.app.ability.Want'; import common from '@ohos.app.ability.common'; import distributedDeviceManager from '@ohos.distributedDeviceManager'; import rpc from '@ohos.rpc'; const TAG: string = '[Page_CollaborateAbility]'; const DOMAIN_NUMBER: number = 0xFF00; const REQUEST_CODE = 1; let dmClass: distributedDeviceManager.DeviceManager; let connectionId: number; 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(); } // 成功連接后臺(tái)服務(wù) hilog.info(DOMAIN_NUMBER, TAG, `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'); } }; function getRemoteDeviceId(): string | undefined { if (typeof dmClass === 'object' && dmClass !== null) { let list = dmClass.getAvailableDeviceListSync(); hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list)); if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null'); return; } if (list.length === 0) { hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`); return; } return list[0].networkId; } else { hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null'); return; } } @Entry @Component struct Page_CollaborateAbility { private context = getContext(this) as common.UIAbilityContext; build() { Column() { //... List({ initialIndex: 0 }) { //... ListItem() { Row() { //... } .onClick(() = > { let want: Want = { 'deviceId': getRemoteDeviceId(), 'bundleName': 'com.samples.stagemodelabilityinteraction', 'abilityName': 'ServiceExtAbility' }; // 建立連接后返回的Id需要保存下來,在解綁服務(wù)時(shí)需要作為參數(shù)傳入 connectionId = this.context.connectServiceExtensionAbility(want, options); }) } //... } //... } //... } }
getRemoteDeviceId方法參照[通過跨設(shè)備啟動(dòng)uiability和serviceextensionability組件實(shí)現(xiàn)多端協(xié)同無返回?cái)?shù)據(jù)]。
- 斷開連接。調(diào)用disconnectServiceExtensionAbility()斷開與后臺(tái)服務(wù)的連接。
import { BusinessError } from '@ohos.base'; import hilog from '@ohos.hilog'; import Want from '@ohos.app.ability.Want'; import common from '@ohos.app.ability.common'; import rpc from '@ohos.rpc'; import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy'; import promptAction from '@ohos.promptAction'; let connectionId: number; const TAG: string = '[Page_CollaborateAbility]'; const DOMAIN_NUMBER: number = 0xFF00; @Entry @Component struct Page_CollaborateAbility { private context = getContext(this) as common.UIAbilityContext; build() { Column() { //... List({ initialIndex: 0 }) { //... ListItem() { Row() { //... } .onClick(() = > { this.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'); }); }) } //... } //... } //... } }
通過跨設(shè)備Call調(diào)用實(shí)現(xiàn)多端協(xié)同
跨設(shè)備Call調(diào)用的基本原理與設(shè)備內(nèi)Call調(diào)用相同,請(qǐng)參見[通過Call調(diào)用實(shí)現(xiàn)UIAbility交互(僅對(duì)系統(tǒng)應(yīng)用開放)]。
下面介紹跨設(shè)備Call調(diào)用實(shí)現(xiàn)多端協(xié)同的方法。
接口說明
表4 Call API接口功能介紹
接口名 | 描述 |
---|---|
startAbilityByCall(want: Want): Promise; | 啟動(dòng)指定UIAbility至前臺(tái)或后臺(tái),同時(shí)獲取其Caller通信接口,調(diào)用方可使用Caller與被啟動(dòng)的Ability進(jìn)行通信。 |
on(method: string, callback: CalleeCallBack): void | 通用組件Callee注冊(cè)method對(duì)應(yīng)的callback方法。 |
off(method: string): void | 通用組件Callee解注冊(cè)method的callback方法。 |
call(method: string, data: rpc.Parcelable): Promise | 向通用組件Callee發(fā)送約定序列化數(shù)據(jù)。 |
callWithResult(method: string, data: rpc.Parcelable): Promise | 向通用組件Callee發(fā)送約定序列化數(shù)據(jù), 并將Callee返回的約定序列化數(shù)據(jù)帶回。 |
release(): void | 釋放通用組件的Caller通信接口。 |
on(type: "release", callback: OnReleaseCallback): void | 注冊(cè)通用組件通信斷開監(jiān)聽通知。 |
開發(fā)步驟
- 需要申請(qǐng)
ohos.permission.DISTRIBUTED_DATASYNC
權(quán)限,配置方式請(qǐng)參見[聲明權(quán)限]。 - 同時(shí)需要在應(yīng)用首次啟動(dòng)時(shí)彈窗向用戶申請(qǐng)授權(quán),使用方式請(qǐng)參見[向用戶申請(qǐng)授權(quán)]。
- 創(chuàng)建被調(diào)用端UIAbility。 被調(diào)用端UIAbility需要實(shí)現(xiàn)指定方法的數(shù)據(jù)接收回調(diào)函數(shù)、數(shù)據(jù)的序列化及反序列化方法。在需要接收數(shù)據(jù)期間,通過on接口注冊(cè)監(jiān)聽,無需接收數(shù)據(jù)時(shí)通過off接口解除監(jiān)聽。
配置UIAbility的啟動(dòng)模式。 配置module.json5,將CalleeAbility配置為單實(shí)例"singleton"。
Json字段 字段說明 “l(fā)aunchType” Ability的啟動(dòng)模式,設(shè)置為"singleton"類型。 UIAbility配置標(biāo)簽示例如下:
"abilities":[{ "name": ".CalleeAbility", "srcEntry": "./ets/CalleeAbility/CalleeAbility.ts", "launchType": "singleton", "description": "$string:CalleeAbility_desc", "icon": "$media:icon", "label": "$string:CalleeAbility_label", "exported": true }]
導(dǎo)入U(xiǎn)IAbility模塊。
import UIAbility from '@ohos.app.ability.UIAbility';
定義約定的序列化數(shù)據(jù)。 調(diào)用端及被調(diào)用端發(fā)送接收的數(shù)據(jù)格式需協(xié)商一致,如下示例約定數(shù)據(jù)由number和string組成。
import type rpc from '@ohos.rpc'; class MyParcelable { num: number = 0; str: string = ''; constructor(num: number, string: string) { this.num = num; this.str = string; } mySequenceable(num: number, string: string): void { this.num = num; this.str = string; } marshalling(messageSequence: rpc.MessageSequence): boolean { messageSequence.writeInt(this.num); messageSequence.writeString(this.str); return true; }; unmarshalling(messageSequence: rpc.MessageSequence): boolean { this.num = messageSequence.readInt(); this.str = messageSequence.readString(); return true; }; };
實(shí)現(xiàn)Callee.on監(jiān)聽及Callee.off解除監(jiān)聽。 如下示例在Ability的onCreate注冊(cè)MSG_SEND_METHOD監(jiān)聽,在onDestroy取消監(jiān)聽,收到序列化數(shù)據(jù)后作相應(yīng)處理并返回。應(yīng)用開發(fā)者根據(jù)實(shí)際業(yè)務(wù)需要做相應(yīng)處理。
import type AbilityConstant from '@ohos.app.ability.AbilityConstant'; import UIAbility from '@ohos.app.ability.UIAbility'; import type Want from '@ohos.app.ability.Want'; import type { Caller } from '@ohos.app.ability.UIAbility'; import hilog from '@ohos.hilog'; import type rpc from '@ohos.rpc'; const TAG: string = '[CalleeAbility]'; const MSG_SEND_METHOD: string = 'CallSendMsg'; const DOMAIN_NUMBER: number = 0xFF00; class MyParcelable { num: number = 0; str: string = ''; constructor(num: number, string: string) { this.num = num; this.str = string; }; mySequenceable(num: number, string: string): void { this.num = num; this.str = string; }; marshalling(messageSequence: rpc.MessageSequence): boolean { messageSequence.writeInt(this.num); messageSequence.writeString(this.str); return true; }; unmarshalling(messageSequence: rpc.MessageSequence): boolean { this.num = messageSequence.readInt(); this.str = messageSequence.readString(); return true; }; }; function sendMsgCallback(data: rpc.MessageSequence): rpc.Parcelable { hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'CalleeSortFunc called'); // 獲取Caller發(fā)送的序列化數(shù)據(jù) let receivedData: MyParcelable = new MyParcelable(0, ''); data.readParcelable(receivedData); hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `receiveData[${receivedData.num}, ${receivedData.str}]`); let num: number = receivedData.num; // 作相應(yīng)處理 // 返回序列化數(shù)據(jù)result給Caller return new MyParcelable(num + 1, `send ${receivedData.str} succeed`) as rpc.Parcelable; }; export default class CalleeAbility extends UIAbility { caller: Caller | undefined; onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { try { this.callee.on(MSG_SEND_METHOD, sendMsgCallback); } catch (error) { hilog.error(DOMAIN_NUMBER, TAG, '%{public}s', `Failed to register. Error is ${error}`); }; }; //... releaseCall(): void { try { if (this.caller) { this.caller.release(); this.caller = undefined; } hilog.info(DOMAIN_NUMBER, TAG, 'caller release succeed'); } catch (error) { hilog.info(DOMAIN_NUMBER, TAG, `caller release failed with ${error}`); }; }; //... onDestroy(): void { try { this.callee.off(MSG_SEND_METHOD); hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Callee OnDestroy'); this.releaseCall(); } catch (error) { hilog.error(DOMAIN_NUMBER, TAG, '%{public}s', `Failed to register. Error is ${error}`); }; }; };
- 獲取Caller接口,訪問被調(diào)用端UIAbility。
導(dǎo)入U(xiǎn)IAbility模塊。
import UIAbility from '@ohos.app.ability.UIAbility';
獲取Caller通信接口。 Ability的context屬性實(shí)現(xiàn)了startAbilityByCall方法,用于獲取指定通用組件的Caller通信接口。如下示例通過this.context獲取Ability實(shí)例的context屬性,使用startAbilityByCall拉起Callee被調(diào)用端并獲取Caller通信接口,注冊(cè)Caller的onRelease和onRemoteStateChange監(jiān)聽。應(yīng)用開發(fā)者根據(jù)實(shí)際業(yè)務(wù)需要做相應(yīng)處理。
import { BusinessError } from '@ohos.base'; import { Caller } from '@ohos.app.ability.UIAbility'; import common from '@ohos.app.ability.common'; import hilog from '@ohos.hilog'; import distributedDeviceManager from '@ohos.distributedDeviceManager'; const TAG: string = '[Page_CollaborateAbility]'; const DOMAIN_NUMBER: number = 0xFF00; let caller: Caller | undefined; let dmClass: distributedDeviceManager.DeviceManager; function getRemoteDeviceId(): string | undefined { if (typeof dmClass === 'object' && dmClass !== null) { let list = dmClass.getAvailableDeviceListSync(); hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(dmClass), JSON.stringify(list)); if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') { hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: list is null'); return; } if (list.length === 0) { hilog.info(DOMAIN_NUMBER, TAG, `getRemoteDeviceId err: list is empty`); return; } return list[0].networkId; } else { hilog.info(DOMAIN_NUMBER, TAG, 'getRemoteDeviceId err: dmClass is null'); return; } }; @Entry @Component struct Page_CollaborateAbility { private context = getContext(this) as common.UIAbilityContext; build() { Column() { //... List({ initialIndex: 0 }) { //... ListItem() { Row() { //... } .onClick(() = > { let caller: Caller | undefined; let context = this.context; context.startAbilityByCall({ deviceId: getRemoteDeviceId(), bundleName: 'com.samples.stagemodelabilityinteraction', abilityName: 'CalleeAbility' }).then((data) = > { if (data !== null) { caller = data; hilog.info(DOMAIN_NUMBER, TAG, 'get remote caller success'); // 注冊(cè)caller的release監(jiān)聽 caller.onRelease((msg) = > { hilog.info(DOMAIN_NUMBER, TAG, `remote caller onRelease is called ${msg}`); }); hilog.info(DOMAIN_NUMBER, TAG, 'remote caller register OnRelease succeed'); promptAction.showToast({ message: $r('app.string.CallerSuccess') }); // 注冊(cè)caller的協(xié)同場(chǎng)景下跨設(shè)備組件狀態(tài)變化監(jiān)聽通知 try { caller.onRemoteStateChange((str) = > { hilog.info(DOMAIN_NUMBER, TAG, 'Remote state changed ' + str); }); } catch (error) { hilog.info(DOMAIN_NUMBER, TAG, `Caller.onRemoteStateChange catch error, error.code: ${JSON.stringify(error.code)}, error.message: ${JSON.stringify(error.message)}`); }; } }).catch((error: BusinessError) = > { hilog.error(DOMAIN_NUMBER, TAG, `get remote caller failed with ${error}`); }); }) } //... } //... } //... } }
getRemoteDeviceId方法參照[通過跨設(shè)備啟動(dòng)uiability和serviceextensionability組件實(shí)現(xiàn)多端協(xié)同無返回?cái)?shù)據(jù)]。
- 向被調(diào)用端UIAbility發(fā)送約定序列化數(shù)據(jù)。
向被調(diào)用端發(fā)送Parcelable數(shù)據(jù)有兩種方式,一種是不帶返回值,一種是獲取被調(diào)用端返回的數(shù)據(jù),method以及序列化數(shù)據(jù)需要與被調(diào)用端協(xié)商一致。如下示例調(diào)用Call接口,向Callee被調(diào)用端發(fā)送數(shù)據(jù)。
import UIAbility from '@ohos.app.ability.UIAbility'; import type { Caller } from '@ohos.app.ability.UIAbility'; import type rpc from '@ohos.rpc'; import hilog from '@ohos.hilog'; const TAG: string = '[CalleeAbility]'; const DOMAIN_NUMBER: number = 0xFF00; const MSG_SEND_METHOD: string = 'CallSendMsg'; class MyParcelable { num: number = 0; str: string = ''; constructor(num: number, string: string) { this.num = num; this.str = string; }; mySequenceable(num: number, string: string): void { this.num = num; this.str = string; }; marshalling(messageSequence: rpc.MessageSequence): boolean { messageSequence.writeInt(this.num); messageSequence.writeString(this.str); return true; }; unmarshalling(messageSequence: rpc.MessageSequence): boolean { this.num = messageSequence.readInt(); this.str = messageSequence.readString(); return true; }; }; export default class EntryAbility extends UIAbility { // ... caller: Caller | undefined; async onButtonCall(): Promise< void > { try { let msg: MyParcelable = new MyParcelable(1, 'origin_Msg'); if (this.caller) { await this.caller.call(MSG_SEND_METHOD, msg); } } catch (error) { hilog.info(DOMAIN_NUMBER, TAG, `caller call failed with ${error}`); }; } // ... }
如下示例調(diào)用CallWithResult接口,向Callee被調(diào)用端發(fā)送待處理的數(shù)據(jù)originMsg,并將’CallSendMsg’方法處理完畢的數(shù)據(jù)賦值給backMsg。
import UIAbility from '@ohos.app.ability.UIAbility'; import type { Caller } from '@ohos.app.ability.UIAbility'; import type rpc from '@ohos.rpc'; import hilog from '@ohos.hilog'; const TAG: string = '[CalleeAbility]'; const DOMAIN_NUMBER: number = 0xFF00; const MSG_SEND_METHOD: string = 'CallSendMsg'; let originMsg: string = ''; let backMsg: string = ''; class MyParcelable { num: number = 0; str: string = ''; constructor(num: number, string: string) { this.num = num; this.str = string; }; mySequenceable(num: number, string: string): void { this.num = num; this.str = string; }; marshalling(messageSequence: rpc.MessageSequence): boolean { messageSequence.writeInt(this.num); messageSequence.writeString(this.str); return true; }; unmarshalling(messageSequence: rpc.MessageSequence): boolean { this.num = messageSequence.readInt(); this.str = messageSequence.readString(); return true; }; }; export default class EntryAbility extends UIAbility { // ... caller: Caller | undefined; async onButtonCallWithResult(originMsg: string, backMsg: string): Promise< void > { try { let msg: MyParcelable = new MyParcelable(1, originMsg); if (this.caller) { const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg); hilog.info(DOMAIN_NUMBER, TAG, 'caller callWithResult succeed'); let result: MyParcelable = new MyParcelable(0, ''); data.readParcelable(result); backMsg = result.str; hilog.info(DOMAIN_NUMBER, TAG, `caller result is [${result.num}, ${result.str}]`); } } catch (error) { hilog.info(DOMAIN_NUMBER, TAG, `caller callWithResult failed with ${error}`); }; } // ... } `HarmonyOS與OpenHarmony鴻蒙文檔籽料:mau123789是v直接拿`
- 釋放Caller通信接口。 Caller不再使用后,應(yīng)用開發(fā)者可以通過release接口釋放Caller。
import UIAbility from '@ohos.app.ability.UIAbility'; import type { Caller } from '@ohos.app.ability.UIAbility'; import hilog from '@ohos.hilog'; export default class EntryAbility extends UIAbility { caller: Caller | undefined; releaseCall(): void { try { if (this.caller) { this.caller.release(); this.caller = undefined; } hilog.info(DOMAIN_NUMBER, TAG, 'caller release succeed'); } catch (error) { hilog.info(DOMAIN_NUMBER, TAG, `caller release failed with ${error}`); }; } }
審核編輯 黃宇
-
組件
+關(guān)注
關(guān)注
1文章
527瀏覽量
18263 -
鴻蒙
+關(guān)注
關(guān)注
57文章
2474瀏覽量
43689
發(fā)布評(píng)論請(qǐng)先 登錄
鴻蒙千帆起】《開心消消樂》完成鴻蒙原生應(yīng)用開發(fā),創(chuàng)新多端聯(lián)動(dòng)用戶體驗(yàn)
HarmonyOS NEXT Developer Beta1最新術(shù)語表
為滿足全場(chǎng)景智慧體驗(yàn) 鴻蒙系統(tǒng)一四大技術(shù)特性!
HarmonyOS學(xué)習(xí)之八:鴻蒙系統(tǒng)從1.0到2.0的提升
華為鴻蒙系統(tǒng) HarmonyOS 2.0 京東、美團(tuán)、優(yōu)酷等軟件,分布式跨設(shè)備交互演示及簡(jiǎn)單說明
華為鴻蒙系統(tǒng) HarmonyOS 2.0 京東、美團(tuán)、優(yōu)酷等軟件,分布式跨設(shè)備交互演示及簡(jiǎn)單說明
HarmonyOS應(yīng)用框架如何解決多設(shè)備交互問題?
HarmonyOS原子化服務(wù)卡片開發(fā)-分布式體驗(yàn)學(xué)習(xí)
HDC2021技術(shù)分論壇:廣發(fā)證券攜手HarmonyOS打造智慧金融服務(wù)
新能力讓數(shù)據(jù)多端協(xié)同更便捷,數(shù)據(jù)跨端遷移更高效!
DevEco Studio 2.1跨平臺(tái)設(shè)備交互使用示例
OpenHarmony應(yīng)用模型的構(gòu)成要素與Stage優(yōu)勢(shì)
華為發(fā)布HarmonyOS2,全新桌面支持應(yīng)用跨設(shè)備流轉(zhuǎn)
鴻蒙開發(fā):應(yīng)用組件跨設(shè)備交互(流轉(zhuǎn))【概述】

鴻蒙開發(fā):應(yīng)用組件跨設(shè)備交互(流轉(zhuǎn))【跨端遷移】

評(píng)論