HarmonyOS編程跨設備跳轉 | Java注釋版
前言
這里有HarmonyOS文檔學習 | 分布式任務調度 | 思維導圖,是關于文檔部分的思維導圖,跨設備調用也是用到里面的知識
本文章只是說明書,是代碼的補充部分。不建議僅根據該文進行實踐
正確的做法是下載附件中的源碼,對照源碼和文章的步驟,進行實踐復現
跨設備跳轉是頁面跳轉的進階版,如果頁面跳轉還不熟悉,可以在我的第一彈中練習。在安卓中由于沒有跨設備的接口,所以要實現都需要自己寫。而在鴻蒙中,只需要用一兩行代碼就能實現跨設備的的核心操作
三種跨設備跳轉的區別
只把頁面內容遷移過去
把編輯中的內容遷移過去,支持雙向控制
把編輯中的內容遷移過去,并可撤回遷移,僅支持單向控制
正文
1. 實現步驟
跨設備調用屬于不同頁面跳轉,所以要有兩個以上的Ability
第一步:創建多個Ability,創建后,會在slice、layout和graphic中自動生成對應的文件

第二步:編寫xml布局文件,這里只是簡單的示例,所以就只有簡單的內容
第三步:編寫業務邏輯代碼
1. 聲明權限
因為涉及到跨設備調度,所以為了安全,需要獲取權限才能進行交互
聲明獲取設備列表即設備信息的權限
config.json
- "reqPermissions": [
- {
- "name": "ohos.permission.DISTRIBUTED_DATASYNC"
- },
- {
- "name": "ohos.permission.GET_BUNDLE_INFO"
- },
- {
- "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"
- },
- {
- "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO"
- }
- ]
注意:
第三方應用使用:ohos.permission.DISTRIBUTED_DATASYNC
還需要在主Ability代碼中顯式聲明
- public class MainAbility extends Ability {
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- super.setMainRoute(MainAbilitySlice.class.getName());
- // 為第三方應用權限的主動聲明
- requestPermissionsFromUser(new String[] {"ohos.permission.DISTRIBUTED_DATASYNC"}, 0);
- }
- }
2. 獲取設備Id
只有拿到設備Id,跳轉的時候才會根據Id進行頁面遷移
3. 實現遷移功能代碼
……
補充模擬器如何分布式跨設備測試:
如何多開模擬器,進行分布式應用開發(在DES 2.1版本之前,只能用真機進行分布式跨設備開發)
1. 先確保自己DES版本是2.1以上,可以通過 Help > Check for Updates 進行更新

2. 更新完之后也可以順便更新自己的 SDK 和 Previewer (Previewer這個挺好用的,寫完布局可直接預覽而不用開模擬器)
3. 點擊 SDK Platforms 和 SDK Tools 查看,在這里我都更新了,反正內存多~
4. 都更新完之后,可以去 File > Settings 或者按 Ctrl + Alt + S 快捷鍵進入設置面板,點擊 DevEco Labs,勾選 Enable Super Device(其他也可以勾選,Java Previewr賊好用,在寫完xml布局文件后,點擊最右邊導航欄或者按 Alt + 3 就可以預覽了)

5. 然后就可以看到模擬器上多了 Super Device
2. 核心代碼部分
2.1. 只把頁面內容遷移過去
MainAbilitySlice.java
- // 通過組件id獲得組件
- btn1 = (Button)findComponentById(ResourceTable.Id_migration_btn_01);
- // 設置按鈕的點擊監聽事件
- btn1.setClickedListener(component -> {
- // 要實現跨設備的打開FA
- // 第一步當然就是要獲取到設備id
- // 通過設備管理的獲得設備列表方法獲得
- List<DeviceInfo> deviceList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
- // 判空
- if (deviceList.isEmpty()) {
- return null;
- }
- // 獲得設備數量
- int deviceNum = deviceList.size();
- // 創建兩個列表,分別存設備id和設備名
- ArrayList<String> deviceIds = new ArrayList<>(deviceNum);
- ArrayList<String> deviceNames = new ArrayList<>(deviceNum);
- // 遍歷設備列表,將設備id和設備名分別存到兩個列表中
- deviceList.forEach(device -> {
- deviceIds.add(device.getDeviceId());
- deviceNames.add(device.getDeviceName());
- });
- // 我就直接使用deviceIds的第一個元素,作為啟動遠程設備的目標id
- String deviceId = deviceIds.get(0);
- if (deviceId != null) {
- // 新建Intent,意圖:信息的載體
- Intent btn_1_intent = new Intent();
- // OperationBuilder()方式
- Operation operation = new Intent.OperationBuilder()
- .withDeviceId(deviceId) // 獲得設備id,在本地的時候可以為空
- .withBundleName("com.anzia.study_2") // 包名,在config.json中可找到
- .withAbilityName("com.anzia.study_2.RemoteAbility") // 設立目標頁面,一定要路徑名!!!
- .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) // 設立標簽,允許跨設備
- .build(); // 構建
- btn_1_intent.setOperation(operation); // 把operation存進intent中
- startAbility(btn_1_intent);
- }
- });
測試結果:
這里用的是模擬器,項目要在兩個模擬上都進行打包安裝
安裝好后會提示用戶獲取權限,獲得權限才能獲取設備Id,從而進行跨設備頁面跳轉

點擊第一個按鈕,即可看到另一設備啟動

2.2. 把編輯中的內容遷移過去,支持雙向控制
由于獲取設備Id的這部分代碼經常使用,所以可以把這部分封裝為一個工具類
如果要獲取設備Id,只需要實現這個工具類即可
DeviceUtils.java
- public class DeviceUtils {
- public static String getDeviceId() {
- // 通過設備管理的獲得設備列表方法獲得
- List<DeviceInfo> deviceList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
- // 判空
- if (deviceList.isEmpty()) {
- return null;
- }
- // 獲得設備數量
- int deviceNum = deviceList.size();
- // 創建兩個列表,分別存設備id和設備名
- ArrayList<String> deviceIds = new ArrayList<>(deviceNum);
- ArrayList<String> deviceNames = new ArrayList<>(deviceNum);
- // 遍歷設備列表,將設備id和設備名分別存到兩個列表中
- deviceList.forEach(device -> {
- deviceIds.add(device.getDeviceId());
- deviceNames.add(device.getDeviceName());
- });
- // 我就直接使用deviceIds的第一個元素,作為啟動遠程設備的目標id
- String deviceIdStr = deviceIds.get(0);
- return deviceIdStr;
- }
- }
在MainAbilitySlice.java中實現可交互的頁面跳轉
使用new ElementName()設備模板頁面路徑
- btn2 = (Button)findComponentById(ResourceTable.Id_migration_btn_02);
- btn2.setClickedListener(component -> {
- Intent btn_2_intent = new Intent();
- ElementName migrationSliceEn = new ElementName("", "com.anzia.study_2", "MigrationAbility");
- btn_2_intent.setElement(migrationSliceEn);
- startAbility(btn_2_intent);
- });
要把編輯中的文字遷移到其他設備,需要對目標Ability和Slice實現IAbilityContinuation接口,并把構造方法設置為true
MigrationAbility.java

MigrationAbilitySlice.java
- public class MigrationAbilitySlice extends AbilitySlice implements IAbilityContinuation {
- private TextField textField;
- private Button mgBtn;
- private String tfStr = "";
- @Override
- public void onStart(Intent intent) {
- super.onStart(intent);
- super.setUIContent(ResourceTable.Layout_ability_migration);
- // 編輯的文字
- textField = (TextField) findComponentById(ResourceTable.Id_textfield_migration);
- // 默認為空
- textField.setText(tfStr);
- mgBtn = (Button) findComponentById(ResourceTable.Id_migration_button);
- mgBtn.setClickedListener(component -> {
- // 核心代碼,點擊按鈕,實現FA的遷移
- // 第一步獲取設備id
- String deviceId = DeviceUtils.getDeviceId();
- if (deviceId != null) {
- // 實現設備遷移
- continueAbility(deviceId);
- }
- });
- }
- @Override
- public void onActive() {
- super.onActive();
- }
- @Override
- public void onForeground(Intent intent) {
- super.onForeground(intent);
- }
- @Override
- public boolean onStartContinuation() {
- return true;
- }
- @Override
- // 保持數據
- public boolean onSaveData(IntentParams intentParams) {
- // 獲得文本框中的文字,并保存到data中,鍵值對形式
- intentParams.setParam("data", textField.getText());
- return true;
- }
- @Override
- // 恢復數據
- public boolean onRestoreData(IntentParams intentParams) {
- // 在其他設備恢復數據時,從data讀取內容
- tfStr = intentParams.getParam("data").toString();
- return true;
- }
- @Override
- public void onCompleteContinuation(int i) {
- }
- }
測試結果
點擊按鈕進入MigrationAbility頁面,在輸入框中輸入文本內容,再點擊按鈕,可以看到已經遷移過去了
在目標設備上添加新的內容,再按點擊遷移,可以對源設備的內容進行修改。即,可以雙向控制
2.3. 把編輯中的內容遷移過去,并可撤回遷移,僅支持單向控制
實現方式和上面的基本差不多,也要實現IAbilityContinuation接口,在部分細節不同
不同點:
1. 在頁面中多了一個回遷的按鈕
2. 在Slice中
跨設備的方法由continueAbility()變為continueAbilityReversibly()
撤回遷移使用的方法是reverseContinueAbility()
MigrationBackAbilitySlice.java

測試結果:
在文本框輸入數據后,點擊遷移按鈕,就可以在目標設備喚起應用了。點擊撤回按鈕,也可以把目標設備的頁面撤回
這個操作是單向的,不能再遷移后,對目標設備內容修改再點擊遷移修改源設備內容。這樣做會閃退
3. 總結
因為涉及到設備之間的流轉,涉及到安全,所以要在config.json中聲明權限。如果想利用第三方設備,除了要在config.json中聲明,還需要在代碼中顯式聲明
跨設備需要獲取設備Id,如果多個Slice中都要獲取設備Id,那么可以將獲取設備Id的這個步驟封裝為一個工具類
在簡單的跨設備調用,可以使用OperationBuilder()方法即可
要將設備編輯中的內容跨設備的話,需要為目標頁面的Ability和Slice實現IAbilityContinuation()接口,使用new ElementName()
使用continueAbility進行雙向交互
使用continueAbilityReversibly()和reverseContinueAbility()進行單向跨設備跳轉及撤回內容