HarmonyOS編程跨設(shè)備跳轉(zhuǎn) | Java注釋版
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
前言
這里有HarmonyOS文檔學(xué)習(xí) | 分布式任務(wù)調(diào)度 | 思維導(dǎo)圖,是關(guān)于文檔部分的思維導(dǎo)圖,跨設(shè)備調(diào)用也是用到里面的知識
本文章只是說明書,是代碼的補(bǔ)充部分。不建議僅根據(jù)該文進(jìn)行實踐
正確的做法是下載附件中的源碼,對照源碼和文章的步驟,進(jìn)行實踐復(fù)現(xiàn)
跨設(shè)備跳轉(zhuǎn)是頁面跳轉(zhuǎn)的進(jìn)階版,如果頁面跳轉(zhuǎn)還不熟悉,可以在我的第一彈中練習(xí)。在安卓中由于沒有跨設(shè)備的接口,所以要實現(xiàn)都需要自己寫。而在鴻蒙中,只需要用一兩行代碼就能實現(xiàn)跨設(shè)備的的核心操作
三種跨設(shè)備跳轉(zhuǎn)的區(qū)別
只把頁面內(nèi)容遷移過去
把編輯中的內(nèi)容遷移過去,支持雙向控制
把編輯中的內(nèi)容遷移過去,并可撤回遷移,僅支持單向控制
正文
1. 實現(xiàn)步驟
跨設(shè)備調(diào)用屬于不同頁面跳轉(zhuǎn),所以要有兩個以上的Ability
第一步:創(chuàng)建多個Ability,創(chuàng)建后,會在slice、layout和graphic中自動生成對應(yīng)的文件

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

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

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

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

2.2. 把編輯中的內(nèi)容遷移過去,支持雙向控制
由于獲取設(shè)備Id的這部分代碼經(jīng)常使用,所以可以把這部分封裝為一個工具類
如果要獲取設(shè)備Id,只需要實現(xiàn)這個工具類即可
DeviceUtils.java
- public class DeviceUtils {
- public static String getDeviceId() {
- // 通過設(shè)備管理的獲得設(shè)備列表方法獲得
- List<DeviceInfo> deviceList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
- // 判空
- if (deviceList.isEmpty()) {
- return null;
- }
- // 獲得設(shè)備數(shù)量
- int deviceNum = deviceList.size();
- // 創(chuàng)建兩個列表,分別存設(shè)備id和設(shè)備名
- ArrayList<String> deviceIds = new ArrayList<>(deviceNum);
- ArrayList<String> deviceNames = new ArrayList<>(deviceNum);
- // 遍歷設(shè)備列表,將設(shè)備id和設(shè)備名分別存到兩個列表中
- deviceList.forEach(device -> {
- deviceIds.add(device.getDeviceId());
- deviceNames.add(device.getDeviceName());
- });
- // 我就直接使用deviceIds的第一個元素,作為啟動遠(yuǎn)程設(shè)備的目標(biāo)id
- String deviceIdStr = deviceIds.get(0);
- return deviceIdStr;
- }
- }
在MainAbilitySlice.java中實現(xiàn)可交互的頁面跳轉(zhuǎn)
使用new ElementName()設(shè)備模板頁面路徑
- 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);
- });
要把編輯中的文字遷移到其他設(shè)備,需要對目標(biāo)Ability和Slice實現(xiàn)IAbilityContinuation接口,并把構(gòu)造方法設(shè)置為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);
- // 默認(rèn)為空
- textField.setText(tfStr);
- mgBtn = (Button) findComponentById(ResourceTable.Id_migration_button);
- mgBtn.setClickedListener(component -> {
- // 核心代碼,點擊按鈕,實現(xiàn)FA的遷移
- // 第一步獲取設(shè)備id
- String deviceId = DeviceUtils.getDeviceId();
- if (deviceId != null) {
- // 實現(xiàn)設(shè)備遷移
- continueAbility(deviceId);
- }
- });
- }
- @Override
- public void onActive() {
- super.onActive();
- }
- @Override
- public void onForeground(Intent intent) {
- super.onForeground(intent);
- }
- @Override
- public boolean onStartContinuation() {
- return true;
- }
- @Override
- // 保持?jǐn)?shù)據(jù)
- public boolean onSaveData(IntentParams intentParams) {
- // 獲得文本框中的文字,并保存到data中,鍵值對形式
- intentParams.setParam("data", textField.getText());
- return true;
- }
- @Override
- // 恢復(fù)數(shù)據(jù)
- public boolean onRestoreData(IntentParams intentParams) {
- // 在其他設(shè)備恢復(fù)數(shù)據(jù)時,從data讀取內(nèi)容
- tfStr = intentParams.getParam("data").toString();
- return true;
- }
- @Override
- public void onCompleteContinuation(int i) {
- }
- }
測試結(jié)果
點擊按鈕進(jìn)入MigrationAbility頁面,在輸入框中輸入文本內(nèi)容,再點擊按鈕,可以看到已經(jīng)遷移過去了
在目標(biāo)設(shè)備上添加新的內(nèi)容,再按點擊遷移,可以對源設(shè)備的內(nèi)容進(jìn)行修改。即,可以雙向控制
2.3. 把編輯中的內(nèi)容遷移過去,并可撤回遷移,僅支持單向控制
實現(xiàn)方式和上面的基本差不多,也要實現(xiàn)IAbilityContinuation接口,在部分細(xì)節(jié)不同
不同點:
1. 在頁面中多了一個回遷的按鈕
2. 在Slice中
跨設(shè)備的方法由continueAbility()變?yōu)閏ontinueAbilityReversibly()
撤回遷移使用的方法是reverseContinueAbility()
MigrationBackAbilitySlice.java

測試結(jié)果:
在文本框輸入數(shù)據(jù)后,點擊遷移按鈕,就可以在目標(biāo)設(shè)備喚起應(yīng)用了。點擊撤回按鈕,也可以把目標(biāo)設(shè)備的頁面撤回
這個操作是單向的,不能再遷移后,對目標(biāo)設(shè)備內(nèi)容修改再點擊遷移修改源設(shè)備內(nèi)容。這樣做會閃退
3. 總結(jié)
因為涉及到設(shè)備之間的流轉(zhuǎn),涉及到安全,所以要在config.json中聲明權(quán)限。如果想利用第三方設(shè)備,除了要在config.json中聲明,還需要在代碼中顯式聲明
跨設(shè)備需要獲取設(shè)備Id,如果多個Slice中都要獲取設(shè)備Id,那么可以將獲取設(shè)備Id的這個步驟封裝為一個工具類
在簡單的跨設(shè)備調(diào)用,可以使用OperationBuilder()方法即可
要將設(shè)備編輯中的內(nèi)容跨設(shè)備的話,需要為目標(biāo)頁面的Ability和Slice實現(xiàn)IAbilityContinuation()接口,使用new ElementName()
使用continueAbility進(jìn)行雙向交互
使用continueAbilityReversibly()和reverseContinueAbility()進(jìn)行單向跨設(shè)備跳轉(zhuǎn)及撤回內(nèi)容
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)