鴻蒙OS 發(fā)布式任務(wù)調(diào)度開發(fā)指導(dǎo)

2020-09-18 11:00 更新

場(chǎng)景介紹

開發(fā)者在應(yīng)用中集成分布式調(diào)度能力,通過調(diào)用指定能力的分布式接口,實(shí)現(xiàn)跨設(shè)備能力調(diào)度。根據(jù) Ability 模板及意圖的不同,分布式任務(wù)調(diào)度向開發(fā)者提供以下六種能力:?jiǎn)?dòng)遠(yuǎn)程 FA、啟動(dòng)遠(yuǎn)程 PA、關(guān)閉遠(yuǎn)程 PA、連接遠(yuǎn)程 PA、斷開連接遠(yuǎn)程 PA 和 FA 跨設(shè)備遷移。下面以設(shè)備 A(本地設(shè)備)和設(shè)備 B(遠(yuǎn)端設(shè)備)為例,進(jìn)行場(chǎng)景介紹:

  1. 設(shè)備 A 啟動(dòng)設(shè)備 B 的 FA:在設(shè)備 A 上通過本地應(yīng)用提供的啟動(dòng)按鈕,啟動(dòng)設(shè)備 B 上對(duì)應(yīng)的 FA。例如:設(shè)備 A 控制設(shè)備 B 打開相冊(cè),只需開發(fā)者在啟動(dòng) FA 時(shí)指定打開相冊(cè)的意圖即可。
  2. 設(shè)備 A 啟動(dòng)設(shè)備 B 的 PA:在設(shè)備 A 上通過本地應(yīng)用提供的啟動(dòng)按鈕,啟動(dòng)設(shè)備 B 上指定的 PA。例如:開發(fā)者在啟動(dòng)遠(yuǎn)程服務(wù)時(shí)通過意圖指定音樂播放服務(wù),即可實(shí)現(xiàn)設(shè)備 A 啟動(dòng)設(shè)備 B 音樂播放的能力。
  3. 設(shè)備 A 關(guān)閉設(shè)備 B 的 PA:在設(shè)備 A 上通過本地應(yīng)用提供的關(guān)閉按鈕,關(guān)閉設(shè)備 B 上指定的 PA。類似啟動(dòng)的過程,開發(fā)者在關(guān)閉遠(yuǎn)程服務(wù)時(shí)通過意圖指定音樂播放服務(wù),即可實(shí)現(xiàn)關(guān)閉設(shè)備 B 上該服務(wù)的能力。
  4. 設(shè)備 A 連接設(shè)備 B 的 PA:在設(shè)備 A 上通過本地應(yīng)用提供的連接按鈕,連接設(shè)備 B 上指定的 PA。連接后,通過其他功能相關(guān)按鈕實(shí)現(xiàn)控制對(duì)端 PA 的能力。通過連接關(guān)系,開發(fā)者可以實(shí)現(xiàn)跨設(shè)備的同步服務(wù)調(diào)度,實(shí)現(xiàn)如大型計(jì)算任務(wù)互助等價(jià)值場(chǎng)景。
  5. 設(shè)備 A 與設(shè)備 B 的 PA 斷開連接:在設(shè)備 A 上通過本地應(yīng)用提供斷開連接的按鈕,將之前已連接的 PA 斷開連接。
  6. 設(shè)備 A 的 FA 遷移至設(shè)備B:設(shè)備 A 上通過本地應(yīng)用提供的遷移按鈕,將設(shè)備 A 的業(yè)務(wù)無縫遷移到設(shè)備B中。通過業(yè)務(wù)遷移能力,打通設(shè)備 A 和設(shè)備 B 間的壁壘,實(shí)現(xiàn)如文檔跨設(shè)備編輯、視頻從客廳到房間跨設(shè)備接續(xù)播放等場(chǎng)景。

接口說明

分布式調(diào)度平臺(tái)提供的連接和斷開連接 PA、啟動(dòng)遠(yuǎn)程 FA、啟動(dòng)和關(guān)閉 PA 以及遷移 FA 的能力,是實(shí)現(xiàn)更多價(jià)值性場(chǎng)景的基礎(chǔ)。

連接遠(yuǎn)程PA

connectAbility(Intent intent, IAbilityConnection conn)接口提供連接指定設(shè)備上 PA 的能力,Intent 中指定待連接 PA 的設(shè)備 deviceId、bundleName 和 abilityName。當(dāng)連接成功后,通過在 conn 定義的 onAbilityConnectDone 回調(diào)中獲取對(duì)端 PA 的服務(wù)代理,兩者的連接關(guān)系則由 conn 維護(hù)。具體的參數(shù)定義如下表所示:

參數(shù)名 類型 說明
intent ohos.aafwk.content.Intent 開發(fā)者需在 intent 對(duì)應(yīng)的Operation 中指定待連接 PA 的設(shè)備 deviceId、bundleName 和 abilityName。
conn ohos.aafwk.ability.IAbilityConnection 當(dāng)連接成功或失敗時(shí),作為連接關(guān)系的回調(diào)接口。該接口提供連接完成和斷開連接完成時(shí)的處理邏輯,開發(fā)者可根據(jù)具體的場(chǎng)景進(jìn)行定義。

啟動(dòng)遠(yuǎn)程FA/PA

startAbility(Intent intent) 接口提供啟動(dòng)指定設(shè)備上 FA 和 PA 的能力,Intent 中指定待啟動(dòng) FA/PA 的設(shè)備 deviceId、bundleName 和 abilityName。具體參數(shù)定義如下表所示:

參數(shù)名 類型 說明
intent ohos.aafwk.content.Intent 當(dāng)開發(fā)者需要調(diào)用該接口啟動(dòng)遠(yuǎn)程 PA 時(shí),需要指定待啟動(dòng) PA 的設(shè)備 deviceId、bundleName 和 abilityName。若不指定設(shè)備deviceId,則無法跨設(shè)備調(diào)用 PA。類似地,在啟動(dòng)FA時(shí),也需要開發(fā)者指定啟動(dòng) FA 的設(shè)備 deviceId、bundleName 和 abilityName。

分布式調(diào)度平臺(tái)還會(huì)提供與上述功能相對(duì)應(yīng)的斷開遠(yuǎn)程 PA 的連接和關(guān)閉遠(yuǎn)程 PA 的接口,相關(guān)的參數(shù)與連接、啟動(dòng)的接口類似。

  • 斷開遠(yuǎn)程 PA 連接:disconnectAbility (IAbilityConnection conn)。
  • 關(guān)閉遠(yuǎn)程 PA:boolean stopAbility (Intent intent)。

遷移FA

continueAbility(String deviceId)接口提供將本地FA遷移到指定設(shè)備上的能力,需要開發(fā)者在調(diào)用時(shí)指定目標(biāo)設(shè)備的 deviceId。具體參數(shù)定義如下表所示:

說明

Ability 和 AbilitySlice 類均需要實(shí)現(xiàn) IAbilityContinuation 及其方法,才可以實(shí)現(xiàn) FA 遷移。

參數(shù)名 類型 說明
deviceId String 當(dāng)開發(fā)者需要調(diào)用該接口將本地 FA 遷移時(shí),需要指定目標(biāo)設(shè)備的 deviceId。

開發(fā)步驟

  1. 導(dǎo)入功能依賴的包。

  1. // 以下依賴包含分布式調(diào)度平臺(tái)開放的接口,用于:連接/斷開連接遠(yuǎn)程 PA、啟動(dòng)遠(yuǎn)程 FA、通過連接關(guān)系注冊(cè)的回調(diào)函數(shù) onAbilityConnectDon e中返回的對(duì)端 PA 的代理,實(shí)現(xiàn)對(duì)PA的控制
  2. import ohos.aafwk.ability.AbilitySlice;
  3. import ohos.aafwk.ability.IAbilityConnection;
  4. import ohos.aafwk.content.Intent;
  5. import ohos.aafwk.content.Operation;
  6. import ohos.bundle.ElementName;
  7. // 為了實(shí)現(xiàn)遷移能力,需要引入傳遞遷移所需數(shù)據(jù)的包以及實(shí)現(xiàn)遷移能力的接口。
  8. import ohos.aafwk.ability.IAbilityContinuation;
  9. import ohos.aafwk.content.IntentParams;
  10. // 為了實(shí)現(xiàn)跨設(shè)備指令及數(shù)據(jù)通信,需要集成 HarmonyOS 提供的 RPC 接口
  11. import ohos.rpc.IRemoteObject;
  12. import ohos.rpc.IRemoteBroker;
  13. import ohos.rpc.MessageParcel;
  14. import ohos.rpc.MessageOption;
  15. import ohos.rpc.RemoteException;
  16. import ohos.rpc.RemoteObject;
  17. //(可選)多設(shè)備場(chǎng)景下涉及設(shè)備選擇,為此需要引入組網(wǎng)設(shè)備發(fā)現(xiàn)的能力
  18. import ohos.distributedschedule.interwork.DeviceInfo;
  19. import ohos.distributedschedule.interwork.DeviceManager;
  20. // (可選)設(shè)計(jì)界面相關(guān)的包函數(shù),對(duì) FA 界面及按鈕進(jìn)行繪制
  21. import ohos.agp.components.Button;
  22. import ohos.agp.components.Component;
  23. import ohos.agp.components.Component.ClickedListener;
  24. import ohos.agp.components.ComponentContainer.LayoutConfig;
  25. import ohos.agp.components.element.ShapeElement;
  26. import ohos.agp.components.PositionLayout;

  1. (可選)編寫一個(gè)基本的 FA 用于使用分布式能力。

  1. // 調(diào)用 AbilitySlice 模板實(shí)現(xiàn)一個(gè)用于控制基礎(chǔ)功能的 FA
  2. // Ability 和 AbilitySlice 類均需要實(shí)現(xiàn) IAbilityContinuation 及其方法,才可以實(shí)現(xiàn) FA 遷移。AbilitySlice 的代碼示例如下
  3. public class SampleSlice extends AbilitySlice implements IAbilityContinuation {
  4. @Override
  5. public void onStart(Intent intent) {
  6. super.onStart(intent);
  7. // 開發(fā)者可以自行進(jìn)行界面設(shè)計(jì)
  8. // 為按鈕設(shè)置統(tǒng)一的背景色
  9. // 例如通過PositionLayout指定大小可以實(shí)現(xiàn)簡(jiǎn)單界面
  10. PositionLayout layout = new PositionLayout(this);
  11. LayoutConfig config = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);
  12. layout.setLayoutConfig(config);
  13. ShapeElement buttonBg = new ShapeElement();
  14. buttonBg.setRgbColor(new RgbColor(0,125,255));
  15. addComponents(layout, buttonBg, config);
  16. super.setUIContent(layout);
  17. }
  18. @Override
  19. public void onInactive() {
  20. super.onInactive();
  21. }
  22. @Override
  23. public void onActive() {
  24. super.onActive();
  25. }
  26. @Override
  27. public void onBackground() {
  28. super.onBackground();
  29. }
  30. @Override
  31. public void onForeground(Intent intent) {
  32. super.onForeground(intent);
  33. }
  34. @Override
  35. public void onStop() {
  36. super.onStop();
  37. }
  38. }

說明

此步驟展示了一個(gè)簡(jiǎn)單 FA 的實(shí)現(xiàn)過程,實(shí)際開發(fā)中請(qǐng)開發(fā)者根據(jù)需要進(jìn)行設(shè)計(jì)。

  1. (可選)為不同的能力設(shè)置相應(yīng)的控制按鈕。

  1. // 建議開發(fā)者按照自己的界面進(jìn)行按鈕設(shè)計(jì)
  2. // 開發(fā)者可以自行實(shí)現(xiàn)如 createButton 的方法,新建一個(gè)顯示文字 text,背景色為 buttonBg 以及大小尺寸位置符合 config 設(shè)置的按鈕,用來與用戶發(fā)生交互
  3. // private Button createButton(String text, ShapeElement buttonBg, LayoutConfig config)
  4. // 按照順序在 PositionLayout 中依次添加按鈕的示例
  5. private void addComponents(PositionLayout linear, ShapeElement buttonBg, LayoutConfig config) {
  6. // 構(gòu)建遠(yuǎn)程啟動(dòng)FA的按鈕
  7. btnStartRemoteFA = createButton("StartRemoteFA", buttonBg, config);
  8. btnStartRemoteFA.setClickedListener(mStartRemoteFAListener);
  9. linear.addComponent(btnStartRemoteFA);
  10. // 構(gòu)建遠(yuǎn)程啟動(dòng)PA的按鈕
  11. btnStartRemotePA = createButton("StartRemotePA", buttonBg, config);
  12. btnStartRemotePA.setClickedListener(mStartRemotePAListener);
  13. linear.addComponent(btnStartRemotePA);
  14. // 構(gòu)建遠(yuǎn)程關(guān)閉PA的按鈕
  15. btnStopRemotePA = createButton("StopRemotePA", buttonBg, config);
  16. btnStopRemotePA.setClickedListener(mStopRemotePAListener);
  17. linear.addComponent(btnStopRemotePA);
  18. // 構(gòu)建連接遠(yuǎn)程PA的按鈕
  19. btnConnectRemotePA = createButton("ConnectRemotePA", buttonBg, config);
  20. btnConnectRemotePA.setClickedListener(mConnectRemotePAListener);
  21. linear.addComponent(btnConnectRemotePA);
  22. // 構(gòu)建控制連接PA的按鈕
  23. btnControlRemotePA = createButton("ControlRemotePA", buttonBg, config);
  24. btnControlRemotePA.setClickedListener(mControlPAListener);
  25. linear.addComponent(btnControlRemotePA);
  26. // 構(gòu)建與遠(yuǎn)程PA斷開連接的按鈕
  27. btnDisconnectRemotePA = createButton("DisconnectRemotePA", buttonBg, config);
  28. btnDisconnectRemotePA.setClickedListener(mDisconnectRemotePAListener);
  29. linear.addComponent(btnDisconnectRemotePA);
  30. // 構(gòu)建遷移FA的按鈕
  31. btnContinueRemoteFA = createButton("ContinueRemoteFA", buttonBg, config);
  32. btnContinueRemoteFA.setClickedListener(mContinueAbilityListener);
  33. linear.addComponent(btnContinueRemoteFA);
  34. }

說明

此處只展示了基于按鈕控制的能力調(diào)度方法,實(shí)際開發(fā)中請(qǐng)開發(fā)者根據(jù)需要選擇能力調(diào)度方式。代碼示例中未體現(xiàn)按鈕如位置、樣式等具體的設(shè)置方法,詳請(qǐng)參考 JAVA UI框架。

  1. 通過設(shè)備管理 DeviceManager 提供的 getDeviceList 接口獲取設(shè)備列表,用于指定目標(biāo)設(shè)備。

  1. // ISelectResult 是一個(gè)自定義接口,用來處理指定設(shè)備 deviceId 后執(zhí)行的行為
  2. interface ISelectResult {
  3. void onSelectResult(String deviceId);
  4. }
  5. // 獲得設(shè)備列表,開發(fā)者可在得到的在線設(shè)備列表中選擇目標(biāo)設(shè)備執(zhí)行操作
  6. private void scheduleRemoteAbility(ISelectResult listener) {
  7. // 調(diào)用DeviceManager的getDeviceList接口,通過FLAG_GET_ONLINE_DEVICE標(biāo)記獲得在線設(shè)備列表
  8. List<DeviceInfo> onlineDevices = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
  9. // 判斷組網(wǎng)設(shè)備是否為空
  10. if (onlineDevices.isEmpty()) {
  11. listener.onSelectResult(null);
  12. return;
  13. }
  14. int numDevices = onlineDevices.size();
  15. ArrayList<String> deviceIds = new ArrayList<>(numDevices);
  16. ArrayList<String> deviceNames = new ArrayList<>(numDevices);
  17. onlineDevices.forEach((device) -> {
  18. deviceIds.add(device.getDeviceId());
  19. deviceNames.add(device.getDeviceName());
  20. });
  21. // 以選擇首個(gè)設(shè)備作為目標(biāo)設(shè)備為例
  22. // 開發(fā)者也可按照具體場(chǎng)景,通過別的方式進(jìn)行設(shè)備選擇
  23. String selectDeviceId = deviceIds.get(0);
  24. listener.onSelectResult(selectDeviceId);
  25. }

上述實(shí)例中涉及對(duì)在線組網(wǎng)設(shè)備的查詢,該項(xiàng)能力需要開發(fā)者在對(duì)應(yīng)的 config.json 中聲明獲取設(shè)備列表及設(shè)備信息的權(quán)限,如下所示:

  1. {
  2. "reqPermissions": [
  3. {
  4. "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"
  5. },
  6. {
  7. "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
  8. },
  9. {
  10. "name": "ohos.permission.GET_BUNDLE_INFO"
  11. }
  12. ]
  13. }

  1. 為啟動(dòng)遠(yuǎn)程 FA 的按鈕設(shè)置點(diǎn)擊回調(diào),實(shí)現(xiàn)啟動(dòng)遠(yuǎn)程 FA 的能力。

  1. // 啟動(dòng)一個(gè)指定 bundleName 和 abilityName 的 FA
  2. private ClickedListener mStartRemoteFAListener = new ClickedListener() {
  3. @Override
  4. public void onClick(Component arg0) {
  5. // 啟動(dòng)遠(yuǎn)程PA
  6. scheduleRemoteAbility(new ISelectResult() {
  7. @Override
  8. void onSelectResult(String deviceId) {
  9. if (deviceId != null) {
  10. Intent intent = new Intent();
  11. // 通過scheduleRemoteAbility指定目標(biāo)設(shè)備deviceId
  12. // 指定待啟動(dòng)FA的bundleName和abilityName
  13. // 例如:bundleName = "com.huawei.helloworld"
  14. // abilityName = "com.huawei.helloworld.SampleFeatureAbility"
  15. // 設(shè)置分布式標(biāo)記,表明當(dāng)前涉及分布式能力
  16. Operation operation = new Intent.OperationBuilder()
  17. .withDeviceId(deviceId)
  18. .withBundleName(bundleName)
  19. .withAbilityName(abilityName)
  20. .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
  21. .build();
  22. intent.setOperation(operation);
  23. // 通過AbilitySlice包含的startAbility接口實(shí)現(xiàn)跨設(shè)備啟動(dòng)FA
  24. startAbility(intent);
  25. }
  26. }
  27. });
  28. }
  29. };

  1. 為啟動(dòng)和關(guān)閉 PA 定義回調(diào),實(shí)現(xiàn)啟動(dòng)和關(guān)閉 PA 的能力。

對(duì)于 PA 的啟動(dòng)、關(guān)閉、連接等操作都需要開發(fā)者提供目標(biāo)設(shè)備的 deviceId。開發(fā)者可以通過 DeviceManager 相關(guān)接口得到當(dāng)前組網(wǎng)下的設(shè)備列表,并以彈窗的形式供用戶選擇,也可以按照實(shí)際需要實(shí)現(xiàn)其他個(gè)性化的處理方式。在點(diǎn)擊事件回調(diào)函數(shù)中,需要開發(fā)者指定得到 deviceId 后的處理邏輯,即實(shí)現(xiàn)類似上例中 listener.onSelectResult(String deviceId) 的方法,代碼示例如下:

  1. // 啟動(dòng)遠(yuǎn)程 PA
  2. private ClickedListener mStartRemotePAListener = new ClickedListener() {
  3. @Override
  4. public void onClick(Component arg0) {
  5. // 啟動(dòng)遠(yuǎn)程PA
  6. scheduleRemoteAbility(new ISelectResult() {
  7. @Override
  8. void onSelectResult(String deviceId) {
  9. if (deviceId != null) {
  10. Intent intentToStartPA = new Intent();
  11. // bundleName和abilityName與待啟動(dòng)PA對(duì)應(yīng)
  12. // 例如:bundleName = "com.huawei.helloworld"
  13. // abilityName = "com.huawei.helloworld.SampleParticleAbility"
  14. Operation operation = new Intent.OperationBuilder()
  15. .withDeviceId(deviceId)
  16. .withBundleName(bundleName)
  17. .withAbilityName(abilityName)
  18. .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
  19. .build();
  20. intentToStartPA.setOperation(operation);
  21. startAbility(intentToStartPA);
  22. }
  23. }
  24. });
  25. }
  26. };
  27. // 關(guān)閉遠(yuǎn)程 PA,和啟動(dòng)類似開發(fā)者需要指定待關(guān)閉 PA 對(duì)應(yīng)的 bundleName 和 abilityName
  28. private ClickedListener mStopRemotePAListener = new ClickedListener() {
  29. @Override
  30. public void onClick(Component arg0) {
  31. scheduleRemoteAbility(new ISelectResult() {
  32. @Override
  33. void onSelectResult(String deviceId) {
  34. if (deviceId != null) {
  35. Intent intentToStopPA = new Intent();
  36. // bundleName和abilityName與待關(guān)閉PA對(duì)應(yīng)
  37. // 例如:bundleName = "com.huawei.helloworld"
  38. // abilityName = "com.huawei.helloworld.SampleParticleAbility"
  39. Operation operation = new Intent.OperationBuilder()
  40. .withDeviceId(deviceId)
  41. .withBundleName(bundleName)
  42. .withAbilityName(abilityName)
  43. .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
  44. .build();
  45. intentToStopPA.setOperation(operation);
  46. stopAbility(intentToStopPA);
  47. }
  48. }
  49. });
  50. }
  51. };

說明

啟動(dòng)和關(guān)閉的行為類似,開發(fā)者只需在 Intent 中指定待調(diào)度 PA 的 deviceId、bundleName 和 abilityName,并以 operation 的形式封裝到 Intent 內(nèi)。通過 AbilitySlice(Ability)包含的 startAbility()和 stopAbility()接口即可實(shí)現(xiàn)相應(yīng)功能。

  1. 設(shè)備 A 連接設(shè)備 B 側(cè)的 PA,利用連接關(guān)系調(diào)用該 PA 執(zhí)行特定任務(wù),以及斷開連接。

  1. // 當(dāng)連接完成時(shí),用來提供管理已連接 PA 的能力
  2. private MyRemoteProxy mProxy = null;
  3. // 用于管理連接關(guān)系
  4. private IAbilityConnection conn = new IAbilityConnection() {
  5. @Override
  6. public void onAbilityConnectDone(ElementName element, IRemoteObject remote, int resultCode) {
  7. // 跨設(shè)備PA連接完成后,會(huì)返回一個(gè)序列化的IRemoteObject對(duì)象
  8. // 通過該對(duì)象得到控制遠(yuǎn)端服務(wù)的代理
  9. mProxy = new MyRemoteProxy(remote);
  10. btnConnectRemotePA.setText("connectRemoteAbility done");
  11. }
  12. @Override
  13. public void onAbilityDisconnectDone(ElementName element, int resultCode) {
  14. // 當(dāng)已連接的遠(yuǎn)端PA關(guān)閉時(shí),會(huì)觸發(fā)該回調(diào)
  15. // 支持開發(fā)者按照返回的錯(cuò)誤信息進(jìn)行PA生命周期管理
  16. disconnectAbility(conn);
  17. }
  18. };

僅通過啟動(dòng)/關(guān)閉兩種方式對(duì) PA 進(jìn)行調(diào)度無法應(yīng)對(duì)需長(zhǎng)期交互的場(chǎng)景,因此,分布式任務(wù)調(diào)度平臺(tái)向開發(fā)者提供了跨設(shè)備PA連接及斷開連接的能力。為了對(duì)已連接 PA 進(jìn)行管理,開發(fā)者需要實(shí)現(xiàn)一個(gè)滿足 IAbilityConnection 接口的連接狀態(tài)檢測(cè)實(shí)例,通過該實(shí)例可以對(duì)連接及斷開連接完成時(shí)設(shè)置具體的處理邏輯,例如:獲取控制對(duì)端 PA 的代理等。進(jìn)一步為了使用該代理跨設(shè)備調(diào)度 PA,開發(fā)者需要在本地及對(duì)端分別實(shí)現(xiàn)對(duì)外接口一致的代理。一個(gè)具備加法能力的代理示例如下:

  1. // 以連接提供加法計(jì)算能力的 PA 為例。為了提供跨設(shè)備連接能力,需要在本地發(fā)起連接側(cè)和對(duì)端被連接側(cè)分別實(shí)現(xiàn)代理。
  2. // 發(fā)起連接側(cè)的代理示例如下
  3. public class MyRemoteProxy implements IRemoteBroker{
  4. private static final int ERR_OK = 0;
  5. private static final int COMMAND_PLUS = IRemoteObject.MIN_TRANSACTION_ID;
  6. private final IRemoteObject remote;
  7. public MyRemoteProxy(
  8. /* [in] */ IRemoteObject remote) {
  9. this.remote = remote;
  10. }
  11. @Override
  12. public IRemoteObject asObject() {
  13. return remote;
  14. }
  15. public int plus(
  16. /* [in] */ int a,
  17. /* [in] */ int b) throws RemoteException {
  18. MessageParcel data = MessageParcel.obtain();
  19. MessageParcel reply = MessageParcel.obtain();
  20. // option不同的取值,決定采用同步或異步方式跨設(shè)備控制PA
  21. // 本例需要同步獲取對(duì)端PA執(zhí)行加法的結(jié)果,因此采用同步的方式,即MessageOption.TF_SYNC
  22. // 具體MessageOption的設(shè)置,可參考相關(guān)API文檔
  23. MessageOption option = new MessageOption(MessageOption.TF_SYNC);
  24. data.writeInt(a);
  25. data.writeInt(b);
  26. try {
  27. remote.sendRequest(COMMAND_PLUS, data, reply, option);
  28. int ec = reply.readInt();
  29. if (ec != ERR_OK) {
  30. throw new RemoteException();
  31. }
  32. int result = reply.readInt();
  33. return result;
  34. } catch (RemoteException e) {
  35. throw new RemoteException();
  36. } finally {
  37. data.reclaim();
  38. reply.reclaim();
  39. }
  40. }
  41. }

此外,對(duì)端待連接的 PA 需要實(shí)現(xiàn)對(duì)應(yīng)的客戶端,代碼示例如下所示:

  1. // 以計(jì)算加法為例,對(duì)端實(shí)現(xiàn)的客戶端如下
  2. public class MyRemote extends RemoteObject implements IRemoteBroker{
  3. private static final int ERR_OK = 0;
  4. private static final int ERROR = -1;
  5. private static final int COMMAND_PLUS = IRemoteObject.MIN_TRANSACTION_ID;
  6. public MyRemote() {
  7. super("MyService_Remote");
  8. }
  9. @Override
  10. public IRemoteObject asObject() {
  11. return this;
  12. }
  13. @Override
  14. public boolean onRemoteRequest(int code, MessageParcel data, MessageParcel reply, MessageOption option) {
  15. if (code != COMMAND_PLUS) {
  16. reply.writeInt(ERROR);
  17. return false;
  18. }
  19. int value1 = data.readInt();
  20. int value2 = data.readInt();
  21. int sum = value1 + value2;
  22. reply.writeInt(ERR_OK);
  23. reply.writeInt(sum);
  24. return true;
  25. }
  26. }

對(duì)端除了要實(shí)現(xiàn)如上所述的客戶端外,待連接的 PA 還需要作如下修改:

  1. // 為了返回給連接方可調(diào)用的代理,需要在該 PA 中實(shí)例化客戶端,例如作為該 PA 的成員變量
  2. private MyProxy remote = new MyProxy();
  3. // 當(dāng)該 PA 接收到連接請(qǐng)求時(shí),即將該客戶端轉(zhuǎn)化為代理返回給連接發(fā)起側(cè)
  4. @Override
  5. protected IRemoteObject onConnect(Intent intent) {
  6. super.onConnect(intent);
  7. return remote.asObject();
  8. }

完成上述步驟后,可以通過點(diǎn)擊事件實(shí)現(xiàn)連接、利用連接關(guān)系控制 PA 以及斷開連接等行為,代碼示例如下:

  1. // 連接遠(yuǎn)程PA
  2. private ClickedListener mConnectRemotePAListener = new ClickedListener() {
  3. @Override
  4. public void onClick(Component arg0) {
  5. scheduleRemoteAbility(new ISelectResult() {
  6. @Override
  7. void onSelectResult(String deviceId) {
  8. if (deviceId != null) {
  9. Intent connectPAIntent = new Intent();
  10. // bundleName和abilityName與待連接的PA一一對(duì)應(yīng)
  11. // 例如:bundleName = "com.huawei.helloworld"
  12. // abilityName = "com.huawei.helloworld.SampleParticleAbility"
  13. Operation operation = new Intent.OperationBuilder()
  14. .withDeviceId(deviceId)
  15. .withBundleName(bundleName)
  16. .withAbilityName(abilityName)
  17. .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
  18. .build();
  19. connectPAIntent.setOperation(operation);
  20. connectAbility(connectPAIntent, conn);
  21. }
  22. }
  23. });
  24. }
  25. };
  26. // 控制已連接PA執(zhí)行加法
  27. private ClickedListener mControlPAListener = new ClickedListener() {
  28. @Override
  29. public void onClick(Component arg0) {
  30. if (mProxy != null) {
  31. int ret = -1;
  32. try {
  33. ret = mProxy.plus(10, 20);
  34. } catch (RemoteException e) {
  35. e.printStackTrace();
  36. }
  37. btnControlRemotePA.setText("ControlRemotePA result = " + ret);
  38. }
  39. }
  40. };
  41. // 與遠(yuǎn)程PA斷開連接
  42. private ClickedListener mDisconnectRemotePAListener = new ClickedListener() {
  43. @Override
  44. public void onClick(Component arg0) {
  45. // 按鈕復(fù)位
  46. btnConnectRemotePA.setText("ConnectRemotePA");
  47. btnControlRemotePA.setText("ControlRemotePA");
  48. disconnectAbility(conn);
  49. }
  50. };

說明

通過連接/斷開連接遠(yuǎn)程 PA,與跨設(shè)備 PA 建立長(zhǎng)期的管理關(guān)系。例如在本例中,通過連接關(guān)系得到遠(yuǎn)程 PA 的控制代理后,實(shí)現(xiàn)跨設(shè)備計(jì)算加法并將結(jié)果返回到本地顯示。在實(shí)際開發(fā)中,開發(fā)者可以根據(jù)需要實(shí)現(xiàn)多種分布式場(chǎng)景,例如:跨設(shè)備位置/電量等信息的采集、跨設(shè)備計(jì)算資源互助等。

  1. 設(shè)備 A 將運(yùn)行時(shí)的 FA 遷移到設(shè)備 B,實(shí)現(xiàn)業(yè)務(wù)在設(shè)備間無縫遷移。

  1. // 跨設(shè)備遷移FA
  2. // 本地FA設(shè)置當(dāng)前運(yùn)行任務(wù)
  3. private ClickedListener mContinueAbilityListener = new ClickedListener() {
  4. @Override
  5. public void onClick(Component arg0) {
  6. // 用戶選擇設(shè)備后實(shí)現(xiàn)業(yè)務(wù)遷移
  7. scheduleRemoteAbility(new ISelectResult() {
  8. @Override
  9. public void onSelectResult(String deviceId) {
  10. continueAbility(deviceId);
  11. }
  12. });
  13. }
  14. };

此外,不同于啟動(dòng)行為,F(xiàn)A 的遷移還涉及到狀態(tài)數(shù)據(jù)的傳遞。為此,繼承的 IAbilityContinuation 接口為開發(fā)者提供遷移過程中特定事件的管理能力。通過自定義遷移事件相關(guān)的行為,最終實(shí)現(xiàn)對(duì) Ability 的遷移。具體的定義可以參考相關(guān)的 API 文檔,此處主要以較為常用的兩個(gè)事件,包括遷移發(fā)起端完成遷移的回調(diào) onCompleteContinuation(int result)以及接收到遠(yuǎn)端遷移行為傳遞數(shù)據(jù)的回調(diào) onRestoreData(IntentParams restoreData)。其他還包括遷移到遠(yuǎn)端設(shè)備的 FA 關(guān)閉的回調(diào) onRemoteTerminated()、用于本地遷移發(fā)起時(shí)保存狀態(tài)數(shù)據(jù)的回調(diào) onSaveData(IntentParams saveData)和本地發(fā)起遷移的回調(diào) onStartContinuation()。按照實(shí)際應(yīng)用自定義特定場(chǎng)景對(duì)應(yīng)的回調(diào),可以完成多種場(chǎng)景下 FA 的遷移任務(wù)。

  1. @Override
  2. public boolean onSaveData(IntentParams saveData) {
  3. String exampleData = String.valueOf(System.currentTimeMillis());
  4. saveData.setParam("continueParam", exampleData);
  5. return true;
  6. }
  7. @Override
  8. public boolean onRestoreData(IntentParams restoreData) {
  9. // 遠(yuǎn)端FA遷移傳來的狀態(tài)數(shù)據(jù),開發(fā)者可以按照特定的場(chǎng)景對(duì)這些數(shù)據(jù)進(jìn)行處理
  10. Object data = restoreData.getParam("continueParam");
  11. return true;
  12. }
  13. @Override
  14. public void onCompleteContinuation(int result) {
  15. btnContinueRemoteFA.setText("ContinueAbility Done");
  16. }

說明

  • FA 遷移可以打通設(shè)備間的壁壘,有助于不同能力的設(shè)備進(jìn)行互助。前文以一個(gè)簡(jiǎn)單的例子介紹如何通過分布式任務(wù)調(diào)度提供的能力,實(shí)現(xiàn) FA 跨設(shè)備的遷移(包括 FA 啟動(dòng)及狀態(tài)數(shù)據(jù)的同步)。
  • FA 遷移過程中,遠(yuǎn)端 FA 首先接收到發(fā)起端 FA 傳輸?shù)臄?shù)據(jù),再執(zhí)行啟動(dòng),即 onRestoreData() 發(fā)生在 onStart() 之前,詳見 API 參考。
以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)