Harmony OS 分布式操作(跨设备拉起以及Ability迁移) 精华

为什么问问题要用为什么
发布于 2022-1-10 18:14
浏览
1收藏

权限配置以及申请

首先设备要分布式申请权限,和Abilities平级写在config.json中。
权限配置相关文档

在这简述:
{允许应用程序与其他设备交换用户数据(如图像、音乐、视频和应用程序数据)}
{允许设备状态改变}
{允许获取其他设备信息(Id、name等)}
{允许非系统应用程序查询有关其他应用程序的信息。}

"reqPermissions": [
      {"name": "ohos.permission.DISTRIBUTED_DATASYNC"},
      {"name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE"},
      {"name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" },
      {"name": "ohos.permission.GET_BUNDLE_INFO"}
    ]

在MainAbility中添加一行申请权限代码

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);
    }
}

void requestPermissionsFromUser (String[] permissions, int requestCode)
接口功能:向系统权限管理模块申请权限(接口可支持一次申请多个。若下一步操作涉及到多个敏感权限,可以这么用,其他情况建议不要这么用。因为弹框还是按权限组一个个去弹框,耗时比较长。用到哪个权限就去申请哪个)

输入参数: permissions:权限名列表;requestCode: 请求应答会带回此编码以匹配本次申请的权限请求

输出参数:无

返回值:无

权限申请配置完成。

获取在线设备Id

首先分布式操作肯定要获取其他在线设备信息(Id等)通过Id找到设备交互

这个可以单独另写一个工具类来获取在线设备Id(具体看代码注释)
​​​​Harmony OS 分布式操作(跨设备拉起以及Ability迁移)-鸿蒙开发者社区

public class CommonTools {
//    获取软总线上的设备ID
    public static String getDeviceId(){
//        将在线设备获取下来存入List(不包括本机)
        List<DeviceInfo> deviceList = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
/*      
        获取软总线中本机的设备ID(需要添加参数 context)
        KvManagerConfig kvManagerConfig = new KvManagerConfig(context);
        kvManagerConfig.getUserInfo().getUserId();
*/
        if (deviceList.isEmpty()){
            return null;
        }
        int deviceNum = deviceList.size();
//        ArrayList 类是一个可以动态修改的数组,没有固定大小的限制,可以添加或删除元素。
        List<String> deviceIds = new ArrayList<>(deviceNum);
        List<String> deviceNames = new ArrayList<>(deviceNum);
//        forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数
        deviceList.forEach((device) ->{
            deviceIds.add(device.getDeviceId());
            deviceNames.add(device.getDeviceName());
        });
//        因为只开了两个虚拟设备,直接选用了列表里第0个device(即远端设备)作为要启动的远程设备
        String deviceIdStr = deviceIds.get(0);
        return deviceIdStr;
    }
}

设置按钮监听事件

进行主页布局设置,主页就设置两个按钮分别展示拉起和迁移两个功能。
略过UI布局,接下来是对按钮进行事件监听
对onClick接口进行重新,因为是两个按钮,所以要判断是点击了哪个按钮,用一个switch判断。

private Component.ClickedListener myClickedListener = new Component.ClickedListener() {
        @Override
        public void onClick(Component component) {
            int component_id = component.getId();
            switch (component_id){
                case ResourceTable.Id_main_start_fa_btn:{
                    Intent startFaDe = new Intent();
//                    此时deviceId已经获取到了,调用上边写好获取在线设备Id的方法
                    Operation op = new Intent.OperationBuilder()
                            .withDeviceId(deviceId)
                            .withBundleName("com.example.myconnect")
                            .withAbilityName("com.example.myconnect.RemoteAbility")
                            .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
                            .build();
                    startFaDe.setOperation(op);
                    startFaDe.setParam("msg","我跨设备拉起你这个FA了");
                    startAbility(startFaDe);
                    break;
                }
                case ResourceTable.Id_main_migration_btn:{
                    Intent openSubMigrationPage = new Intent();
                    openSubMigrationPage.setElement(new ElementName(
//                            是否跨设备,空字符串表示不跨设备,否则传设备ID
                            "",
//                            包名
                            "com.example.myconnect",
//                            跳转到哪一个Ability
                            "com.example.myconnect.MigrationAbility"
                    ));
                    startAbility(openSubMigrationPage);
                    break;
                }
            }
        }
    };

拉起远端设备FeatureAbility

演示过程
先要在两个设备上都运行一下 ,让两个设备都安装上hap包
打开其中一个设备的应用,弹出权限访问dialog,允许访问,然后点击启动远程设备FA
Harmony OS 分布式操作(跨设备拉起以及Ability迁移)-鸿蒙开发者社区
Harmony OS 分布式操作(跨设备拉起以及Ability迁移)-鸿蒙开发者社区

成功拉起另一个设备的RemoteAbility。
在这个地方他是在89设备的MainAbility执行操作,在MainAbilitySlice点击第一个按钮,执行拉起功能,拉起设备88的RemoteAnility,渲染RemoteAbilitySlice(XML没做改动即你好世界)。

页面迁移

点击第二个按钮进入MigrationAbilitySlice(一个TextField和两个Button)
Harmony OS 分布式操作(跨设备拉起以及Ability迁移)-鸿蒙开发者社区

在文本中输入一些文字,点击迁移,效果如下,会在另一台设备拉起该页且会将文本数据通过软总线传输过去,给用户的使用好像就像一台设备,就好像一台设备通过底层总线进行传输数据。
Harmony OS 分布式操作(跨设备拉起以及Ability迁移)-鸿蒙开发者社区

在远端设备进行输入,然后在本地端点击迁回,会关闭远端并且将远端文本传回
Harmony OS 分布式操作(跨设备拉起以及Ability迁移)-鸿蒙开发者社区

迁移页面的后端代码以及注释:

//(implements)要实现 IAbilityContinuation接口才能实现迁移
public class MigrationAbilitySlice extends AbilitySlice implements IAbilityContinuation {
    private Button btn;
    private Button back_btn;
    private TextField textField;
    private String msg;
    private String deviceId;
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_migration);
//        获取在线设备Id
        deviceId = CommonTools.getDeviceId();
//        找到布局中的三个组件(一个Text以及两个Button)
        textField = findComponentById(ResourceTable.Id_migration_textfield);
        btn = findComponentById(ResourceTable.Id_migration_migration_btn);
        back_btn = findComponentById(ResourceTable.Id_migration_migration_back_btn);
//        对两个Button进行监听,重写onClick方法
        btn.setClickedListener(myClickedlistener);
        back_btn.setClickedListener(myClickedlistener);
//        在这个地方对应后边onRestoreData()方法,在这个地方设置Text的文本而不是在onRestoreData方法中设置文本,
//        因为迁移操作属于子线程,要在子线程中改变主线程里的UI组件可能会出问题
        textField.setText(msg);

    }

    Component.ClickedListener myClickedlistener = new Component.ClickedListener() {
        @Override
        public void onClick(Component component) {
            if (deviceId != null){
                switch (component.getId()){
                    case ResourceTable.Id_migration_migration_btn:{
/*
                          单纯迁移
                          continueAbility(deviceId);
 */
//                        迁移
                        continueAbilityReversibly(deviceId);
                        break;
                    }
                    case ResourceTable.Id_migration_migration_back_btn:{
//                        迁回
                        reverseContinueAbility();
                        break;
                    }
                }
            }
        }
    };

    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }
    //    开始迁移时的回调函数,返回true才会执行下边onSaveData回调
    @Override
    public boolean onStartContinuation() {
        return true;
    }
    //    将发起迁移页面上的数据保存时回调,返回true才会接着执行onRestoreData
    @Override
    public boolean onSaveData(IntentParams intentParams) {
        String m = textField.getText();
        intentParams.setParam("msg",m);
        return true;
    }
    //    在远端恢复数据时回调,返回ture才会执行(远端)
    @Override
    public boolean onRestoreData(IntentParams intentParams) {
        msg = intentParams.getParam("msg").toString();
        return true;
    }
    //    迁移完成之后才会执行(本机)
    @Override
    public void onCompleteContinuation(int i) {

    }
}

最后同样还要在MigrationAbility中实现IAbilityContinuation接口才可以支持迁移

public class MigrationAbility extends Ability implements IAbilityContinuation {
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setMainRoute(MigrationAbilitySlice.class.getName());
    }
    @Override
    public boolean onStartContinuation() {
        return true;
    }
    @Override
    public boolean onSaveData(IntentParams intentParams) {
        return true;
    }
    @Override
    public boolean onRestoreData(IntentParams intentParams) {
        return true;
    }
    @Override
    public void onCompleteContinuation(int i) {

    }
}

1
收藏 1
回复
举报
回复
    相关推荐