【鸿蒙基地】鸿蒙跨设备启动窗口:Page Ability

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

 [[374061]]

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

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

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

HarmonyOS的核心特性(或称为卖点)之一就是软总线技术,而Page Ability的跨设备迁移是软总线的一个具体技术实现。所谓跨设备迁移Page Ability,是指设备A中的特定App调用设备B中该App的Page Ability。这有一个前提,就是设备A和设备B都安装了同一个App。如果B设备没有安装App,B设备就会自动从华为应用商店下载这个App,当然,这一过程是完全静默的。下载完后,就会自动启动相应的Page Ability。这种技术不仅可以启动另一个设备上的Page Ability,还可以向另一个设备中的Page Ability传递数据。

这种技术的一个主要应用场景是,可以将在设备A上完成了一半的工作,迁移到设备B上继续完成。例如,在家中平板电脑上要回一封EMail,但临时有急事,需要出门,这时可以将在平板电脑上写了一半的EMail迁移到手机上,需要在路上完成剩下的工作。

1. 跨设备迁移前的准备工作

在进行跨设备迁移之前,需要为HarmonyOS设备做一下准备:

(1) 打开HarmonyOS设备中的蓝牙;

(2)HarmonyOS设备需要连入Wi-Fi,而且多个HarmonyOS需要在同一个网段;

(3)多个HarmonyOS设备需要用同一个华为开发者账号登录,如图1所示。


图1 用同一个华为开发者账号登录

(4)点击“设置”>“更多连接”>“多设备协同”,进入多设备协同窗口,打开多设备协同开关,如图2所示。

图2 多设备协同

(5)修改HarmonyOS设备名。点击“设置”>“蓝牙”>“设备名称”,进入设备名称窗口,输入一个新的什么名称,如图3所示。尽管这一步不是必须的,但如果拥有多部HarmonyOS设备,可能很多HarmonyOS设备的名称是相同或相近的。为了更好区分不同的HarmonyOS设备,建议修改HarmonyOS设备名称。

图3 修改HarmonyOS设备名称

2 获取设备列表

跨设备迁移是通过设备ID来区分不同设备的,所以首先要获取所有可用的设备的ID。获取设备ID需要调用DeviceManager.getDeviceList方法,该方法返回一个List对象,类型是DeviceInfo,用来描述设备的相关信息,包括设备ID、设备名称(就是上一节设置的设备名称)等。实现代码如下:

  1. List<DeviceInfo> deviceInfoList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); 

getDeviceList方法有一个参数,是一个int类型的值,表示获取什么状态的设备的信息。可以指定的值如下:

(1) DeviceInfo.FLAG_GET_ONLINE_DEVICE:获取所有在线设备的信息;

(2) DeviceInfo. FLAG_GET_OFFLINE_DEVICE:获取所有离线设备的信息;

(3) DeviceInfo. FLAG_GET_ALL_DEVICE:获取所有设备的信息;

通常会使用第1个值,获取所有在线设备的信息,因为只有设备在线,才能将Page Ability迁移到该设备上。

下面给出一个案例,该案例实现了一个通用的显示可用设备列表的Page Ability,点击某一个设备,会返回该设备的ID,

在device_ids.xml布局文件中放置了一个ListContainer组件,用于显示获取的所有可用设备的相关信息。实现代码如下:

  1. public class DeviceIdsAbility extends Ability { 
  2.     // 保存获取到的所有设备的信息 
  3.     private List<DeviceInfo> deviceInfos; 
  4.     private ListContainer listContainerDeviceIds; 
  5.     // 获取所有可用的设备的相关信息 
  6.     public static List<DeviceInfo> getAvailableDeviceIds() { 
  7.         List<DeviceInfo> deviceInfoList = 
  8.                 DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE); 
  9.         if (deviceInfoList == null) { 
  10.             return new ArrayList<>(); 
  11.         } 
  12.         if (deviceInfoList.size() == 0) { 
  13.             return new ArrayList<>(); 
  14.         } 
  15.         return deviceInfoList; 
  16.     } 
  17.     @Override 
  18.     public void onStart(Intent intent) { 
  19.         super.onStart(intent); 
  20.         super.setUIContent(ResourceTable.Layout_device_ids); 
  21.         deviceInfos = getAvailableDeviceIds(); 
  22.         listContainerDeviceIds = 、 
  23.         (ListContainer)findComponentById(ResourceTable.Id_listcontainer_deviceids); 
  24.         if(listContainerDeviceIds != null) { 
  25.             // 为ListContainer组件设置列表项监听器 
  26.             listContainerDeviceIds.setItemClickedListener(new ListContainer.ItemClickedListener() { 
  27.                 @Override 
  28.                 public void onItemClicked(ListContainer listContainer, Component component, int i, long l) { 
  29.                     // 当单击某个列表项(设备)后,会获取该设备的ID,并将这个ID作为Page Ability 
  30.                     // 的结果返回 
  31.                     String deviceId = deviceInfos.get(i).getDeviceId(); 
  32.                     Intent intent = new Intent(); 
  33.                     intent.setParam("deviceId", deviceId); 
  34.                     setResult(100,intent); 
  35.                     // 关闭当前的Page Ability 
  36.                     terminateAbility(); 
  37.   
  38.                 } 
  39.             }); 
  40.             // 为ListContainer组件设置Provider 
  41.             listContainerDeviceIds.setItemProvider(new RecycleItemProvider() { 
  42.                 @Override 
  43.                 public int getCount() { 
  44.                     return deviceInfos.size(); 
  45.                 } 
  46.   
  47.                 @Override 
  48.                 public Object getItem(int i) { 
  49.                     return deviceInfos.get(i); 
  50.                 } 
  51.   
  52.                 @Override 
  53.                 public long getItemId(int i) { 
  54.                     return i; 
  55.                 } 
  56.   
  57.                 @Override 
  58.                 public Component getComponent(int i, Component component, ComponentContainer componentContainer) { 
  59.                     if(component == null) { 
  60.                         // 如果component为null,说明没有可以利用的列表项视图,所以要从布局文件 
  61.                         // 装载一个新的视图对象 
  62.                         component = (DirectionalLayout)LayoutScatter.getInstance(DeviceIdsAbility.this).parse(ResourceTable.Layout_device_id_item,null,false); 
  63.                     } 
  64.                     Text textDeviceName = (Text)component.findComponentById(ResourceTable.Id_text_device_name); 
  65.                     Text textDeviceId = (Text)component.findComponentById(ResourceTable.Id_text_device_id); 
  66.                     if(textDeviceName != null) { 
  67.                         // 显示设备名 
  68.                         textDeviceName.setText(deviceInfos.get(i).getDeviceName()); 
  69.                     } 
  70.                     if(textDeviceId != null) { 
  71.                         // 显示设备ID 
  72.                         textDeviceId.setText(deviceInfos.get(i).getDeviceId()); 
  73.                     } 
  74.                     return component; 
  75.                 } 
  76.             }); 
  77.   
  78.         } 
  79.   
  80.     } 

 在DeviceIdsAbility类中为ListContainer组件装载列表项时,在getComponent方法中利用了第2个参数component,该参数就是列表项的根视图。如果component为null,表明并没有可以利用的列表项视图,所以要创建一个新的列表项视图。如果不为null,表明可以利用其他的还没有显示的列表项视图,只需要替换该视图的Text组件中显示的信息即可。

最后在config.json文件中添加一些与分布式相关的权限。

  1.  "reqPermissions": [ 
  2.       { 
  3.         "name""ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" 
  4.       }, 
  5.       { 
  6.         "name""com.huawei.permission.ACCESS_DISTRIBUTED_ABILITY_GROUP" 
  7.       }, 
  8.       { 
  9.         "name""ohos.permission.DISTRIBUTED_DATASYNC" 
  10.       } 

 运行程序,会看到如图4所示的设备列表。


图4 获取可用设备的ID

要注意的是,通过DeviceManager.getDeviceList方法只能获取其他设备的信息,不能获取自身的信息,例如,有设备A、设备B和设备C。在设备A中只能获取设备B和设备C的信息,而不能获取设备A的信息。在设备B和设备C中的表现也类似。

3 根据设备ID调用Page Ability

一个Page Ability要想跨设备访问,必须实现IAbilityContinuation接口,否则会抛出异常。该接口必须实现的有4个方法,他们的含义如下:

  1. public interface IAbilityContinuation { 
  2.     // 开始迁移,如果返回true,表示可以开始迁移 
  3.     boolean onStartContinuation(); 
  4.     // 开始传递数据,如果返回true,表示成功传递数据 
  5.     boolean onSaveData(IntentParams var1); 
  6.     // 开始恢复数据,如果返回true,表示成功恢复数据 
  7.     boolean onRestoreData(IntentParams var1); 
  8.     // 已经完成Page Ability迁移 
  9.     void onCompleteContinuation(int var1);  

 假设在设备A上将Page Ability迁移到设备B。onStartContinuation方法和onSaveData方法是在设备A上被调用的,而onRestoreData方法和onCompleteContinuation方法是在设备B上被调用的。为了迁移Page Ability,需要在设备A上执行下面的代码:

  1. continueAbility(deviceId); 

其中deviceID是设备ID。当调用该方法后,在设备A上就会依次调用onStartContinuation方法和onSaveData方法,在设备B上会依次调用onRestoreData方法和onCompleteContinuation方法。其中onSaveData方法和onRestoreData方法都有一个IntentParams类型的参数,通过该参数可以在设备A和设备B之间通过Page Ability传递数据(使用方式与Intent类似)。通常在onRestoreData方法中恢复Page Ability从设备A上迁移到设备B上时的数据。

下面给出一个实际的案例,在Page Ability上放置了一个TextField组件,并在该组件中输入了一些文本,然后点击按钮,将该Page Ability迁移到另一部HarmonyOS手机上,并恢复迁移时的数据。

实现代码如下:

  1. public class CrossDevicePageAbility extends Ability implements IAbilityContinuation { 
  2.     private List<DeviceInfo> deviceInfos; 
  3.     private ListContainer listContainerDeviceIds; 
  4.     private TextField textFieldContent; 
  5.     private String content; 
  6.     // 授权方法 
  7.     private void requestPermission() { 
  8.         // 实现Page Ability跨设备迁移,必须用Java代码申请下面的权限 
  9.         // 否则不会有任何反应 
  10.         String[] permission = { 
  11.                 "ohos.permission.DISTRIBUTED_DATASYNC"}; 
  12.         List<String> applyPermissions = new ArrayList<>(); 
  13.         for (String element : permission) { 
  14.             // 验证自身是否已经获得了该权限 
  15.             if (verifySelfPermission(element) != 0) { 
  16.                 if (canRequestPermission(element)) { 
  17.                     // 如果未获得权限,将该权限添加到权限列表 
  18.                     applyPermissions.add(element); 
  19.                 } else { 
  20.                 } 
  21.             } else { 
  22.             } 
  23.         } 
  24.         // 申请相应权限 
  25.         requestPermissionsFromUser(applyPermissions.toArray(new String[0]), 0); 
  26.     } 
  27.     // 要想成功跨设备迁移Page Ability,该方法必须返回true 
  28.     @Override 
  29.     public boolean onStartContinuation() { 
  30.         return true
  31.     }     
  32.     @Override 
  33.     public boolean onSaveData(IntentParams intentParams) { 
  34.         // 保存要传递的数据 
  35.         intentParams.setParam("content",textFieldContent.getText()); 
  36.         return true
  37.     } 
  38.     @Override 
  39.     public boolean onRestoreData(IntentParams intentParams) { 
  40.         // 获取传递过来的数据 
  41.         content = String.valueOf(intentParams.getParam("content")); 
  42.         return true
  43.     } 
  44.     @Override 
  45.     public void onCompleteContinuation(int i) { 
  46.   
  47.     } 
  48.     @Override 
  49.     protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) { 
  50.         // 当选择设备后,利用返回的设备ID迁移Page Ability 
  51.         if(resultCode == 100 && requestCode == 99) { 
  52.             // 获取设备ID 
  53.             String deviceId = resultData.getStringParam("deviceId"); 
  54.             Tools.showTip(this, deviceId); 
  55.             // 跨设备迁移Page Ability 
  56.             continueAbility(deviceId); 
  57.         } 
  58.     } 
  59.   
  60.     @Override 
  61.     public void onStart(Intent intent) { 
  62.         super.onStart(intent); 
  63.         super.setUIContent(ResourceTable.Layout_cross_device_page_ability); 
  64.         // 申请权限 
  65.         requestPermission(); 
  66.         Button button = 
  67.           (Button)findComponentById(ResourceTable.Id_button_cross_device_page_ability); 
  68.         if(button != null) { 
  69.             button.setClickedListener(new Component.ClickedListener() { 
  70.                 @Override 
  71.                 public void onClick(Component component) { 
  72.                     // 显示列表列表窗口 
  73.                     Intent intentPageAbility = new Intent(); 
  74.                     Operation operation = new Intent.OperationBuilder() 
  75.                             .withBundleName("com.unitymarvel.demo"
  76.                             .withAbilityName("com.unitymarvel.demo.ability.DeviceIdsAbility"
  77.                             .build(); 
  78.                     intentPageAbility.setOperation(operation); 
  79.                     startAbilityForResult(intentPageAbility,99); 
  80.                 } 
  81.             }); 
  82.         } 
  83.   
  84.         textFieldContent = (TextField)findComponentById(ResourceTable.Id_textfield_content); 
  85.         if(textFieldContent != null) { 
  86.             // 恢复TextField组件中的数据 
  87.             textFieldContent.setText(content); 
  88.         } 
  89.     } 

 阅读这段代码,需要了解下面几点:

(1)要想成功迁移Page Ability,并成功传递数据。onStartContinuation方法、onSaveData方法和onRestoreData方法都必须返回true,如果读者使用IDE的自动生成代码功能,默认这几个方法都会返回false,请将他们的返回值改成true;

(2)在HarmonyOS中有一些权限,并不是在config.json中声明就可以了,还需要使用Java代码申请,例如,Page Ability跨设备迁移就需要使用Java代码申请ohos.permission.DISTRIBUTED_DATASYNC权限。如果是第一次申请,会弹出如图5的授权对话框,点击“始终允许”按钮关闭该对话框,第2次申请权限,就不会弹出该对话框了;

(3)由于onRestoreData方法在onStart方法之前调用,所以不能直接在onRestoreData方法中使用组件对象,因为组件对象通常都是在onStart方法中创建的。所以在onRestoreData方法被调用时,这些组件对象还都是空。正确的做法是在onRestoreData方法中将要恢复的数据保存到成员变量中,然后在onStart方法中创建完组件对象后,用这些变量恢复组件中的数据。

(4)本例考虑了多部HarmonyOS设备迁移的问题,所以使用了上一节编写的设备列表窗口。在开始跨设备迁移Page Ability之前,会先弹出一个设备列表窗口,当用户选择一个设备后,会返回该设备的ID,然后在onAbilityResult方法中获取这个返回的设备ID,最后使用continueAbility方法迁移Page Ability;

图5 授权对话框

现在运行程序,关闭授权对话框,并在TextField组件中输入一些内容,最后点击“跨设备迁移Page Ability”按钮,会弹出一个设备列表窗口,选择相应的设备后,会在选中的设备中弹出同样的Page Ability,并且TextField组件的数据与原设备上的完全相同,如图6所示。注意,只要被调用方安装了App,不管设备是否已经启动了App,否会自动弹出这个被迁移的Page Ability。

图6 跨设备迁移Page Ability的效果

欢迎访问李宁老师的【鸿蒙基地】专栏:https://harmonyos.51cto.com/column/17

李宁老师的课程主页:https://edu.51cto.com/lecturer/974126.html

[[374064]]

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

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

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

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

 

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

2021-01-12 09:42:05

鸿蒙HarmonyOSPage Abilit

2020-12-28 11:19:06

鸿蒙HarmonyOSPage Abilit

2020-11-05 10:05:25

App

2021-07-01 09:19:56

鸿蒙HarmonyOS应用

2021-06-28 14:41:36

鸿蒙HarmonyOS应用

2021-06-16 15:18:03

鸿蒙HarmonyOS应用

2021-06-23 15:48:08

鸿蒙HarmonyOS应用

2021-07-08 09:42:04

鸿蒙HarmonyOS应用

2022-07-27 22:03:53

HarmonyOS鸿蒙

2021-01-06 11:21:56

鸿蒙HarmonyOS应用开发

2021-03-23 09:52:39

鸿蒙HarmonyOS应用开发

2022-09-07 15:28:02

Stage模型Ability

2021-03-29 09:59:09

鸿蒙HarmonyOS应用开发

2021-10-28 14:48:46

鸿蒙HarmonyOS应用

2021-06-18 14:55:57

鸿蒙HarmonyOS应用

2020-12-29 11:51:16

鸿蒙HarmonyOS华为手机

2021-02-03 09:59:02

鸿蒙HarmonyOS应用开发

2021-08-18 16:20:17

HarmonyOS鸿蒙

2021-05-28 17:01:49

鸿蒙HarmonyOS应用

2012-08-16 13:34:09

云基地
点赞
收藏

51CTO技术栈公众号