暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

HarmonyOS流转,替你踩坑了!!!

鸿蒙技术社区 2021-11-08
460

流转在 HarmonyOS 中泛指多设备分布式操作,也是 HarmonyOS 的亮点之一。


流转按体验可以分为跨端迁移和多端协同,这里主要跟大家讲一下如何进行跨端迁移,以及我在项目开发过程中,所遇到的问题与解决方法。


具体概念这里就不做过多的赘述了,大家可以查阅官方文档:
https://developer.harmonyos.com/cn/docs/documentation/doc-guides/hop-overview-0000001092995092


开发步骤


在开发过程中,我们可以根据业务需求分为以下两种场景:

  • 同个 FA 之间的迁移(Ability1—Ability1)

  • 不同 FA 之间的迁移(Ability1—Ability2)


下面给大家介绍一下以上两种场景的具体的开发步骤。

①同个 FA 之间的迁移


同个 FA 之间的迁移是指不同设备端安装了同个 FA,官方文档已经有比较详细的开发步骤,下面只给大家讲一下需要注意的事项及我所遇到的问题避免大家踩坑。


我们在创建完一个 FA 之后,因为我们大部门的业务逻辑都是在 AbilitySlice,所以我们在 Ability 及 AbilitySlice 都要去实现 IAbilityContinuation 接口。


并且将 Ability 中实现的 onStartContinuation()、onSaveData(IntentParams intentParams)、onRestoreData(IntentParams intentParams)的返回值,都设为 true。

public class MainAbility extends Ability implements IAbilityContinuation {

    @Override
    public boolean onStartContinuation() {
        return true;
    }

    @Override
    public boolean onSaveData(IntentParams intentParams) {
        return true;
    }

    @Override
    public boolean onRestoreData(IntentParams intentParams) {
        return true;
    }
    //省略部分代码
     ...
}


在对应的 FA 模块的 config.json 中,配置对应的权限,且在代码中也需要动态申请。
"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"}
]


if (canRequestPermission(SystemPermission.DISTRIBUTED_DATASYNC)) {
    // 是否可以申请弹框授权(首次申请或者用户未选择禁止且不再提示)
    requestPermissionsFromUser(
            new String[]{SystemPermission.DISTRIBUTED_DATASYNC}, PERMISSIONS_REQUEST_DISTRIBUTED);
}


定义相关参数、设置流转任务管理服务回调函数、注册流转任务管理服务、管理流转的目标设备,同时需要在流转结束时解注册流转任务管理服务。

    // 流转应用包名
    private String BUNDLE_NAME = "XXX.XXX.XXX"
    // 注册流转任务管理服务后返回的Ability token
    private int abilityToken; 
    // 用户在设备列表中选择设备后返回的设备ID
    private String selectDeviceId;
    // 获取流转任务管理服务管理类
    private IContinuationRegisterManager continuationRegisterManager;
    // 设置流转任务管理服务设备状态变更的回调
    private IContinuationDeviceCallback continuationDeviceCallback = new IContinuationDeviceCallback() {
    @Override
    public void onDeviceConnectDone(String deviceId, String deviceType) {
        selectDeviceId = deviceId;
        continuationRegisterManager.updateConnectStatus(abilityToken, selectDeviceId, DeviceConnectState.CONNECTING.getState(), null);
        ...
    }

    @Override
    public void onDeviceDisconnectDone(String s) {
        getUITaskDispatcher().asyncDispatch(() -> {
            continuationRegisterManager.updateConnectStatus(abilityToken, selectDeviceId, DeviceConnectState.DIS_CONNECTING.getState(), null);
        });
        unRegisterContinuation();
    }
};
  // 设置注册流转任务管理服务回调
    private RequestCallback requestCallback = new RequestCallback() {
        @Override
        public void onResult(int result) {
            abilityToken = result;
        }
    };
    ...

    @Override
    public void onStart(Intent intent) {
        ...
        continuationRegisterManager = getContinuationRegisterManager();
    }

    @Override
    public void onStop() {
        super.onStop();
        // 解注册流转任务管理服务
        continuationRegisterManager.unregister(abilityToken, null);
        // 断开流转任务管理服务连接
        continuationRegisterManager.disconnect();
    }


在 Api5 的时候 IContinuationDeviceCallback 的回调接口跟官方文档有些出入,当你选择设备后会在 onDeviceConnectDone 返回你所选择的设备 ID 及设备类型。


注册流转服务之后我们便可以调起系统流转选择设备弹窗,可以通过 ExtraParams 对设备进行过滤,如不需要过滤,可不传。

ExtraParams params = new ExtraParams();
String[] devTypes = new String[]{ExtraParams.DEVICETYPE_SMART_PHONE, ExtraParams.DEVICETYPE_SMART_WATCH, ExtraParams.DEVICETYPE_SMART_PAD};
params.setDevType(devTypes);
registerContinuation();
// 显示选择设备列表
continuationRegisterManager.showDeviceList(abilityToken, paramsnew RequestCallback() {
    @Override
    public void onResult(int result
{
    }
});


选择完设备之后会通过上述的 IContinuationDeviceCallback 的 onDeviceConnectDone 方法进行回调。

之后通过 continueAbility 方法传入目标设备的 DeviceID,将运行的 FA 迁移到目标设备,实现业务在设备间无缝迁移。

// 设置流转任务管理服务设备状态变更的回调
private IContinuationDeviceCallback continuationDeviceCallback = new IContinuationDeviceCallback() {
    @Override
    public void onDeviceConnectDone(String deviceId, String deviceType) {
        selectDeviceId = deviceId;
        getUITaskDispatcher().asyncDispatch(() -> {
            continuationRegisterManager.updateConnectStatus(abilityToken, selectDeviceId, DeviceConnectState.CONNECTING.getState(), null);
        });
        if (selectDeviceId != null) {
            continueAbility(selectDeviceId);
        }
        ...
    }

    @Override
    public void onDeviceDisconnectDone(String s) {
          ...
        unRegisterContinuation();
    }

};


在 FA 迁移中我觉得最主要的部分就是状态和数据的传递,要让用户体验到”无缝“的用户体验,需要通过实现 IAbilityContinuation 接口来实现数据的传递。

主要代码如下:

@Override
public boolean onSaveData(IntentParams saveData) {
      //根据业务需求,在这里去设置需要传递的数据
      saveData.setParam("continueParam", continueParam);
      return true;
 }
@Override
public boolean onRestoreData(IntentParams restoreData) {
      // 远端FA迁移传来的状态数据,开发者可以按照自身业务对这些数据进行处理
      Object data = restoreData.getParam("continueParam");
      getUITaskDispatcher().asyncDispatch(() -> {

        });
      return true;
 }


需要注意的是,在 onRestoreData 处理数据更新 UI 的时候,需要在 UI 线程中去更新,否则会报错。

②不同 FA 之间的迁移


在实际开发中可能会因为设备端的部分需求、UI 的不同,例如车机、手机、手表,从而开发了不同的 FA。


不同 FA 之间的迁移几乎与同个 FA 之间迁移配置一致,只是我们的 AbilitySlice 不需要再实现 IAbilityContinuation 接口来实现数据的同步,而是通过 Intent,具体实现如下。


首先我们先在选择设备成功后的回调 IContinuationDeviceCallback 初始化分布式环境。

// 设置流转任务管理服务设备状态变更的回调
private IContinuationDeviceCallback continuationDeviceCallback = new IContinuationDeviceCallback() {
    @Override
    public void onDeviceConnectDone(String deviceId, String deviceType) {
        selectDeviceId = deviceId;
        //省略部分代码
          ...
        try {
            // 初始化分布式环境
            DeviceManager.initDistributedEnvironment(selectDeviceId, new IInitCallback() {
                @Override
                public void onInitSuccess(String success) {

                }

                @Override
                public void onInitFailure(String failure, int result) {
                }
            });
        } catch (RemoteException e) {
            e.printStackTrace();
        }
       ...
    }
  ....
};


之前我们是通过 continueAbility() 方法进行跳转,而现在我们需要通过 Intent 方法进行跳转。

Intent intent = new Intent();
Operation operation = new Intent.OperationBuilder()
        .withDeviceId(deviceId)
        .withBundleName(bundleName)
        .withAbilityName(abilityName)
        .withFlags(Intent.FLAG_ABILITYSLICE_MULTI_DEVICE)
        .build();
intent.setOperation(operation);
IntentParams intentParams = new IntentParams();
//通过IntentParams传递参数
...
startAbility(intent);


在接收方,我们可以通过 onStart(Intent intent) 方法接受传递过来的参数,再根据自己的业务逻辑实现数据同步。

③自定义设备选择弹窗


在实际项目开发中我们也可以自定义流转弹窗样式,但并不推荐这种方式,经测试发现只有在两个设备通过蓝牙连接的时候才能获取到设备列表,只有在特定的场景。


例如手机与车机、手机与手表在实际使用过程中我们基本上是会保持蓝牙连接的,通过这种方式实现流转会更稳定。但如果不能保持蓝牙实时连接的场景则不推荐。


官方 API 提供了 DeviceManager.getDeviceList() 来获取远端设备,具体代码如下。

public static List<DeviceInfo> getDeviceList() {
    // 调用DeviceManager的getDeviceList接口,通过FLAG_GET_ONLINE_DEVICE标记获得在线设备列表
    List<DeviceInfo> onlineDevices = DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ONLINE_DEVICE);
    // 判断组网设备是否为空
    if (onlineDevices == null) {
        LogUtil.e(TAG, "online devices is null");
        return new ArrayList<>();
    }
    return onlineDevices;
}


获取到设备列表后,我们就可以自行实现页面了,在上述的 showDeviceList() 弹出设备列表的位置替换成自己的弹窗即可。

效果展示


效果如下图:

结语


目前在 DevEco Studio 2.1 Release 以上版本已经支持跨端迁移的模拟器了。


如果没有显示出来可以在 Settings-DevEco Labs 勾选 Enable Super Device。


以上过程是在实际开发过程中慢慢摸索得出,如有不对的地方,欢迎在评论区指出,共同探讨(附下载)。
https://harmonyos.51cto.com/posts/9013


作者:曾瑞绅

👇点击关注鸿蒙技术社区👇
了解鸿蒙一手资讯


求分享

求点赞

求在看

文章转载自鸿蒙技术社区,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论