鸿蒙应用开发入门(七):实现跨设备迁移

开发
文章由鸿蒙社区产出,想要了解更多内容请前往:51CTO和华为官方战略合作共建的鸿蒙技术社区https://harmonyos.51cto.com/#zz

[[374071]]

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com/#zz

6.2 跨设备迁移

1. 分布式任务调度概述

在HarmonyOS中,分布式任务调度平台对搭载HarmonyOS的多设备构筑的“超级虚拟终端”提供统一的组件管理能力,为应用定义统一的能力基线、接口形式、数据结构、服务描述语言,屏蔽硬件差异;支持远程启动、远程调用、业务无缝迁移等分布式任务。

2. 实现调度的约束与限制

1)远程调用PA/FA,开发者需要在Intent中设置支持分布式的标记(例如:Intent.FLAG_ABILITYSLICE_MULTI_DEVICE表示该应用支持分布式调度),否则将无法获得分布式能力。

2)开发者通过在config.json中的reqPermissions字段里添加权限申请:

(1)以获取跨设备连接的能力和分布式数据传输的权限。

分布式数据传输的权限:

  1. {"name""ohos.permission.servicebus.ACCESS_SERVICE"

三方应用使用权限:

  1. {"name""ohos.permission.servicebus.DISTRIBUTED_DATASYNC"

系统应用使用权限:

 

  1. {"name""com.huawei.hwddmp.servicebus.BIND_SERVICE"

(2)另外还有三个获取分布式设备信息需要的权限:

  1. {"name""ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"}, 
  2.  
  3. {"name""ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" }, 
  4.  
  5. "name""ohos.permission.GET_BUNDLE_INFO"

 注意:还需要在开发的时候,要在Ability里主动声明,要用到的权限。

3)FA(Feature Ability,Page模板的Ability)的调用支持启动和迁移行为,在进行调度时:

(1)当启动FA时,需要开发者在Intent中指定对端设备的deviceId、bundleName和abilityName。

(2)FA的迁移实现相同bundleName和abilityName的FA跨设备迁移,因此需要指定迁移设备的deviceId。

3. 实现场景介绍

下面以设备A(本地设备)和设备B(远端设备)为例,介绍下面我们要实现的场景:

1)设备A启动设备B的FA:在设备A上通过本地应用提供的启动按钮,启动设备B上对应的FA。

2)设备A的FA迁移至设备B:设备A上通过本地应用提供的迁移按钮,将设备A的业务无缝迁移到设备B中。

3)设备A的FA迁移至设备B,还可以实现主动撤回迁移。

4. 具体实现前先了解要用的接口

1)启动远程FA

startAbility(Intent intent)接口提供启动指定设备上FA和PA的能力,Intent中指定待启动FA的设备deviceId、bundleName和abilityName。

2)迁移FA

continueAbility(String deviceId)接口提供将本地FA迁移到指定设备上的能力,continueAbilityReversibly(String deviceId) 接口提供将本地FA迁移到指定设备上的能力,这种迁移可撤回, reverseContinueAbility()接口提供撤回迁移的能力。

5. 实战远程启动FA页面

1)编程实现上面场景的界面:

ability_main.xml 

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <DirectionalLayout 
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos" 
  4.     ohos:height="match_parent" 
  5.     ohos:width="match_parent" 
  6.     ohos:orientation="vertical"
  7.  
  8.     <Button 
  9.         ohos:id="$+id:migration_btn_01" 
  10.         ohos:height="match_content" 
  11.         ohos:width="300vp" 
  12.         ohos:text="1.启动远程设备的FA" 
  13.         ohos:text_size="20fp" 
  14.         ohos:text_color="#ffffff" 
  15.         ohos:background_element="$graphic:button_bg" 
  16.         ohos:layout_alignment="horizontal_center" 
  17.         ohos:top_padding="8vp" 
  18.         ohos:bottom_padding="8vp" 
  19.         ohos:left_padding="40vp" 
  20.         ohos:right_padding="40vp" 
  21.         ohos:top_margin="20vp" 
  22.         /> 
  23.  
  24.     <Button 
  25.         ohos:id="$+id:migration_btn_02" 
  26.         ohos:height="match_content" 
  27.         ohos:width="300vp" 
  28.         ohos:text="2.迁移到远程设备" 
  29.         ohos:text_size="20fp" 
  30.         ohos:text_color="#ffffff" 
  31.         ohos:background_element="$graphic:button_bg" 
  32.         ohos:layout_alignment="horizontal_center" 
  33.         ohos:top_padding="8vp" 
  34.         ohos:bottom_padding="8vp" 
  35.         ohos:left_padding="40vp" 
  36.         ohos:right_padding="40vp" 
  37.         ohos:top_margin="20vp" 
  38.         /> 
  39.  
  40.  
  41.     <Button 
  42.         ohos:id="$+id:migration_btn_03" 
  43.         ohos:height="match_content" 
  44.         ohos:width="300vp" 
  45.         ohos:text="3.可迁回的迁移远程设备" 
  46.         ohos:text_size="20fp" 
  47.         ohos:text_color="#ffffff" 
  48.         ohos:background_element="$graphic:button_bg" 
  49.         ohos:layout_alignment="horizontal_center" 
  50.         ohos:top_padding="8vp" 
  51.         ohos:bottom_padding="8vp" 
  52.         ohos:left_padding="40vp" 
  53.         ohos:right_padding="40vp" 
  54.         ohos:top_margin="20vp" 
  55.         /> 
  56. </DirectionalLayou 

 button_bg.xml

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <shape  xmlns:ohos="http://schemas.huawei.com/res/ohos" 
  3.         ohos:shape="rectangle"
  4.     <solid ohos:color="#007DFF"/> 
  5.     <corners ohos:radius="40"/> 
  6. </ 

 MigrationAbility和MigrationBackAbility

  1. // 调用AbilitySlice模板实现一个用于控制基础功能的FA 
  2. // Ability和AbilitySlice类均需要实现IAbilityContinuation及其方法,才可以实现FA迁移。AbilitySlice的代码示例如下 
  3. public class SampleSlice extends AbilitySlice implements IAbilityContinuation { 
  4.     @Override 
  5.     public void onStart(Intent intent) { 
  6.         super.onStart(intent); 
  7.         
  8.         super.setUIContent(layout); 
  9.     } 

ability_migration.xml

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <DirectionalLayout 
  3.     xmlns:ohos="http://schemas.huawei.com/res/ohos" 
  4.     ohos:height="match_parent" 
  5.     ohos:width="match_parent" 
  6.     ohos:background_element="#00ffff" 
  7.     ohos:orientation="vertical"
  8.  
  9.     <Text 
  10.         ohos:id="$+id:text_title" 
  11.         ohos:height="match_content" 
  12.         ohos:width="250vp" 
  13.         ohos:background_element="#0088bb" 
  14.         ohos:layout_alignment="horizontal_center" 
  15.         ohos:text="下面是一个可编辑的文本框" 
  16.         ohos:text_size="50" 
  17.         ohos:padding="5vp" 
  18.         ohos:top_margin="30vp" 
  19.         /> 
  20.  
  21.     <TextField 
  22.         ohos:id="$+id:textfield_back" 
  23.         ohos:height="250vp" 
  24.         ohos:width="250vp" 
  25.         ohos:hint="请输入..." 
  26.         ohos:layout_alignment="horizontal_center" 
  27.         ohos:background_element="#ffffff" 
  28.         ohos:text_color="#888888" 
  29.         ohos:text_size="20fp" 
  30.         ohos:padding="5vp" 
  31.         /> 
  32.     <Button 
  33.         ohos:id="$+id:migration_button" 
  34.         ohos:height="match_content" 
  35.         ohos:width="match_content" 
  36.         ohos:text="点击迁移" 
  37.         ohos:text_size="20fp" 
  38.         ohos:text_color="#ffffff" 
  39.         ohos:background_element="$graphic:button_bg" 
  40.         ohos:top_padding="8vp" 
  41.         ohos:bottom_padding="8vp" 
  42.         ohos:left_padding="50vp" 
  43.         ohos:right_padding="50vp" 
  44.         ohos:layout_alignment="horizontal_center" 
  45.         ohos:top_margin="30vp" 
  46.         /> 
  47. </DirectionalLayou 

 ability_migration_back.xml比ability_migration.xml多一个迁回按钮,另外主页上点击按钮跳转等,略...

2)使用分布式能力要求开发者在Ability对应的config.json中声明多设备协同访问的权限:

(1)三方应用部署权限、分布式数据传输的权限、系统应用使用权限的申请。

  1.     "reqPermissions": [ 
  2.         {"name""ohos.permission.DISTRIBUTED_DATASYNC"}, 
  3.         {"name""ohos.permission.servicebus.ACCESS_SERVICE"}, 
  4.         {"name""com.huawei.hwddmp.servicebus.BIND_SERVICE"}      
  5.     ] 

 (2)声明分布式获取设备列表及设备信息的权限,如下所示:

  1.     "reqPermissions": [ 
  2.         {"name""ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"},  
  3.         {"name""ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" },  
  4.         {"name""ohos.permission.GET_BUNDLE_INFO"
  5.     ] 

 (3)对于三方应用还要求在实现Ability的代码中显式声明需要使用的权限。 

  1. public class SampleSlice extends AbilitySlice implements IAbilityContinuation { 
  2.     @Override 
  3.     public void onStart(Intent intent) { 
  4.         // 开发者显示声明需要使用的权限 
  5.         requestPermissionsFromUser(new String[]{"ohos.permission.DISTRIBUTED_DATASYNC"
  6.                                                 "ohos.permission.servicebus.ACCESS_SERVICE"
  7.                                                 "com.huawei.hwddmp.servicebus.BIND_SERVICE"}, 0); 
  8.         super.onStart(intent);         
  9.     } 

 3) 为启动远程FA的按钮添加点击事件,获取设备信息,实现启动远程FA的能力。 

  1. Button btn1 = (Button) findComponentById(ResourceTable.Id_migration_btn_01); 
  2. btn1.setClickedListener(new Component.ClickedListener() { 
  3.     @Override 
  4.     public void onClick(Component component) { 
  5.         // 调用DeviceManager的getDeviceList接口,通过FLAG_GET_ONLINE_DEVICE标记获得在线设备列表 
  6.         List<DeviceInfo> onlineDevices = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); 
  7.         // 判断组网设备是否为空 
  8.         if (onlineDevices.isEmpty()) { 
  9.             return
  10.         } 
  11.         int numDevices = onlineDevices.size(); 
  12.  
  13.         ArrayList<String> deviceIds = new ArrayList<>(numDevices); 
  14.         ArrayList<String> deviceNames = new ArrayList<>(numDevices); 
  15.         onlineDevices.forEach((device) -> { 
  16.             deviceIds.add(device.getDeviceId()); 
  17.             deviceNames.add(device.getDeviceName()); 
  18.         }); 
  19.         // 我们这里只有两个设备,所以选择首个设备作为目标设备 
  20.         // 开发者也可按照具体场景,通过别的方式进行设备选择 
  21.         String selectDeviceId = deviceIds.get(0); 
  22.          
  23.         //获取设备ID,最好放到工具类里,很多地方要用! 
  24.  
  25.         if(selectDeviceId!=null){ 
  26.             Intent intent2 = new Intent(); 
  27.             Operation operation = new Intent.OperationBuilder() 
  28.                     .withDeviceId(selectDeviceId) 
  29.                     .withBundleName("cn.ybzy.hmsdemo"
  30.                     .withAbilityName("cn.ybzy.hmsdemo.RemoteAbility"
  31.                     .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE) 
  32.                     .build(); 
  33.             intent2.setOperation(operation); 
  34.             // 通过AbilitySlice包含的startAbility接口实现跨设备启动FA 
  35.             startAbility(intent2); 
  36.         } 
  37.     } 
  38. }); 

 6. 实战将设备A运行时的FA迁移到设备B,实现业务在设备间无缝迁移。

MigrationAbility

  1. public class MigrationAbility extends Ability implements IAbilityContinuation  { 
  2.     @Override 
  3.     public void onStart(Intent intent) { 
  4.         super.onStart(intent); 
  5.         super.setMainRoute(MigrationAbilitySlice.class.getName()); 
  6.     } 
  7.  
  8.     @Override 
  9.     public boolean onStartContinuation() { 
  10.         return true
  11.     } 
  12.  
  13.     @Override 
  14.     public boolean onSaveData(IntentParams intentParams) { 
  15.         return true
  16.     } 
  17.  
  18.     @Override 
  19.     public boolean onRestoreData(IntentParams intentParams) { 
  20.         return true
  21.     } 
  22.  
  23.     @Override 
  24.     public void onCompleteContinuation(int i) { 
  25.  
  26.     } 

 MigrationAbilitySlice 

  1. public class MigrationAbilitySlice extends AbilitySlice implements IAbilityContinuation { 
  2.     TextField textField; 
  3.     String textStr = "请输入数据..."
  4.     @Override 
  5.     public void onStart(Intent intent) { 
  6.         super.onStart(intent); 
  7.         super.setUIContent(ResourceTable.Layout_ability_migration); 
  8.         textField = (TextField)findComponentById(ResourceTable.Id_textfield_migration); 
  9.         textField.setText(textStr); 
  10.         Button btn = (Button) findComponentById(ResourceTable.Id_migration_button); 
  11.         btn.setClickedListener(new Component.ClickedListener() { 
  12.             @Override 
  13.             public void onClick(Component component) { 
  14.                 String deviceId = getDeviceId(); 
  15.                 if(deviceId!=null){ 
  16.                     continueAbility(deviceId); 
  17.                 } 
  18.             } 
  19.         }); 
  20.  
  21.     } 
  22.  
  23.     private String getDeviceId(){ 
  24.         // 调用DeviceManager的getDeviceList接口,通过FLAG_GET_ONLINE_DEVICE标记获得在线设备列表 
  25.         List<DeviceInfo> onlineDevices = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); 
  26.         // 判断组网设备是否为空 
  27.         if (onlineDevices.isEmpty()) { 
  28.             return null
  29.         } 
  30.         int numDevices = onlineDevices.size(); 
  31.  
  32.         ArrayList<String> deviceIds = new ArrayList<>(numDevices); 
  33.         ArrayList<String> deviceNames = new ArrayList<>(numDevices); 
  34.         onlineDevices.forEach((device) -> { 
  35.             deviceIds.add(device.getDeviceId()); 
  36.             deviceNames.add(device.getDeviceName()); 
  37.         }); 
  38.         // 我们这里只有两个设备,所以选择首个设备作为目标设备 
  39.         // 开发者也可按照具体场景,通过别的方式进行设备选择 
  40.         String selectDeviceId = deviceIds.get(0); 
  41.         return selectDeviceId; 
  42.     } 
  43.  
  44.     @Override 
  45.     public boolean onStartContinuation() { 
  46.         return true
  47.     } 
  48.  
  49.     @Override 
  50.     public boolean onSaveData(IntentParams intentParams) { 
  51.         intentParams.setParam("data",textField.getText()); 
  52.         return true
  53.     } 
  54.  
  55.     @Override 
  56.     public boolean onRestoreData(IntentParams intentParams) { 
  57.         textStr = intentParams.getParam("data").toString(); 
  58.         return true
  59.     } 
  60.  
  61.     @Override 
  62.     public void onCompleteContinuation(int i) { 
  63.  
  64.     } 
  65.  
  66.     @Override 
  67.     public void onRemoteTerminated() { 
  68.  
  69.     } 

 此外,不同于启动行为,FA的迁移还涉及到状态数据的传递。为此,继承的IAbilityContinuation接口为开发者提供迁移过程中特定事件的管理能力。通过自定义迁移事件相关的行为,最终实现对Ability的迁移。主要以较为常用的两个事件,包括迁移发起端完成迁移的回调onCompleteContinuation(int result)以及接收到远端迁移行为传递数据的回调onRestoreData(IntentParams restoreData)。其他还包括迁移到远端设备的FA关闭的回调onRemoteTerminated()、用于本地迁移发起时保存状态数据的回调onSaveData(IntentParams saveData)和本地发起迁移的回调onStartContinuation()。

7. 请求回迁

  1. Button btn1 = (Button) findComponentById(ResourceTable.Id_migration_button_back); 
  2. btn1.setClickedListener(new Component.ClickedListener() { 
  3.     @Override 
  4.     public void onClick(Component component) { 
  5.         String deviceId = DeviceUtils.getDeviceId(); 
  6.         if(deviceId!=null){ 
  7.             continueAbilityReversibly(deviceId);  //可撤回迁移 
  8.         } 
  9.     } 
  10. }); 
  11.  
  12. Button btn2 = (Button) findComponentById(ResourceTable.Id_migration_button_back2); 
  13. btn2.setClickedListener(new Component.ClickedListener() { 
  14.     @Override 
  15.     public void onClick(Component component) { 
  16.         reverseContinueAbility();  //撤回迁移 
  17.     } 
  18. }); 

 1)设备A上的Page请求回迁。

2)系统回调设备B上Page及其AbilitySlice栈中所有AbilitySlice实例的IAbilityContinuation.onStartContinuation()方法,以确认当前是否可以立即迁移。

3)如果可以立即迁移,则系统回调设备B上Page及其AbilitySlice栈中所有AbilitySlice实例的IAbilityContinuation.onSaveData()方法,以便保存回迁后恢复状态必须的数据。

4)如果保存数据成功,则系统在设备A上Page恢复AbilitySlice栈,然后回调IAbilityContinuation.onRestoreData()方法,传递此前保存的数据。

5)如果数据恢复成功,则系统终止设备B上Page的生命周期。

©著作权归作者和HarmonyOS技术社区共同所有,如需转载,请注明出处,否则将追究法律责任

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com/#zz

 

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2020-11-05 10:05:25

App

2021-06-16 15:18:03

鸿蒙HarmonyOS应用

2021-12-21 09:56:47

鸿蒙HarmonyOS应用

2022-10-24 14:54:29

LWIP协议鸿蒙

2020-10-15 09:49:45

HarmonyOS 2设备开发

2014-12-11 11:03:20

Qt跨平台开发

2020-12-25 10:52:28

鸿蒙HarmonyOS应用开发

2024-08-13 15:50:57

2021-11-03 09:51:45

鸿蒙HarmonyOS应用

2022-07-01 17:06:49

Fractio鸿蒙

2021-08-17 10:20:14

鸿蒙HarmonyOS应用

2020-12-24 12:01:16

鸿蒙HarmonyOS应用开发

2014-07-08 09:37:28

跨平台Webhtml5

2015-03-17 09:59:41

跨平台

2020-11-09 11:56:49

HarmonyOS

2021-01-05 10:35:04

鸿蒙HarmonyOS应用开发

2015-09-07 09:21:32

移动应用Android平台模板

2022-08-15 22:09:37

设备开发开发笔记

2011-09-19 10:07:14

Visual StudWindows 8云计算

2021-01-06 10:59:14

鸿蒙HarmonyOSPage Abilit
点赞
收藏

51CTO技术栈公众号