开发步骤
完成环境搭建,在DevEco Studio中,选择手机设备,Empty Feature Ability(Java)模板创建项目,在项目自动创建的MainAbility中实现IAbilityContinuation接口。
public class MainAbility extends Ability implements IAbilityContinuation { private static final int DOMAIN_ID = 0xD001100; private static final HiLogLabel LABEL_LOG = new HiLogLabel(3, DOMAIN_ID, "MainAbility"); @Override public void onStart(Intent intent) { super.onStart(intent); super.setMainRoute(MainAbilitySlice.class.getName()); } // 为了方便演示,不在Ability实现流转逻辑,具体逻辑在AbilitySlice中实现 @Override public boolean onStartContinuation() { HiLog.info(LABEL_LOG, "onStartContinuation called"); return true; } @Override public boolean onSaveData(IntentParams saveData) { HiLog.info(LABEL_LOG, "onSaveData called"); return true; } @Override public boolean onRestoreData(IntentParams restoreData) { HiLog.info(LABEL_LOG, "onRestoreData called"); return true; } @Override public void onCompleteContinuation(int result) { HiLog.info(LABEL_LOG, "onCompleteContinuation called"); } @Override public void onFailedContinuation(int errorCode) { HiLog.info(LABEL_LOG, "onFailedContinuation called"); } }
在AbilitySlice中实现一个用于控制基础功能的页面,以下演示代码逻辑都将在AbilitySlice中实现,代码示例如下:
public class MainAbilitySlice extends AbilitySlice { @Override public void onStart(Intent intent) { super.onStart(intent); // 开发者可以自行进行界面设计 // 为按钮设置统一的背景色 // 例如通过PositionLayout可以实现简单界面 PositionLayout layout = new PositionLayout(this); LayoutConfig config = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT); layout.setLayoutConfig(config); ShapeElement buttonBg = new ShapeElement(); buttonBg.setRgbColor(new RgbColor(0, 125, 255)); super.setUIContent(layout); } @Override public void onInactive() { super.onInactive(); } @Override public void onActive() { super.onActive(); } @Override public void onBackground() { super.onBackground(); } @Override public void onForeground(Intent intent) { super.onForeground(intent); } @Override public void onStop() { super.onStop(); } }
在MainAbility对应的config.json中声明跨端迁移访问的权限:ohos.permission.DISTRIBUTED_DATASYNC。在config.json中的配置如下:
{ "module": { "reqPermissions": [ { "name": "ohos.permission.DISTRIBUTED_DATASYNC", "reason": "need", "usedScene": { "ability": [ "MainAbility" ], "when": "inuse" } } ], ... } ... }
此外,还需要在MainAbility的onStart()中,调用requestPermissionsFromUser()方法向用户申请权限,代码示例如下:
public class MainAbility extends Ability implements IAbilityContinuation { @Override public void onStart(Intent intent) { super.onStart(intent); // 开发者显示声明需要使用的权限 requestPermissionsFromUser(new String[]{"ohos.permission.DISTRIBUTED_DATASYNC"}, 0); ... } ... }
设置流转任务管理服务回调函数,注册流转任务管理服务,管理流转的目标设备,同时需要在流转结束时解注册流转任务管理服务。
public class MainAbilitySlice extends AbilitySlice { // 流转应用包名 private String BUNDLE_NAME = "XXX.XXX.XXX"; // 注册流转任务管理服务后返回的Ability token private int abilityToken; // 用户在设备列表中选择设备后返回的设备ID private String selectDeviceId; // 用户是否已发起可拉回流转流程 private boolean isReversibly = false; // 获取流转任务管理服务管理类 private IContinuationRegisterManager continuationRegisterManager; // 设置流转任务管理服务设备状态变更的回调 private IContinuationDeviceCallback callback = new IContinuationDeviceCallback() { @Override public void onConnected(ContinuationDeviceInfo deviceInfo) { // 在用户选择设备后设置设备ID selectDeviceId = deviceInfo.getDeviceId(); //更新选择设备后的流转状态 continuationRegisterManager.updateConnectStatus(abilityToken, selectDeviceId, DeviceConnectState.CONNECTED.getState(), null); } @Override public void onDisconnected(String deviceId) { } }; // 设置注册流转任务管理服务回调 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(); }
为不同功能设置相应的控制按钮。
// 建议开发者按照自己的界面进行按钮设计,示例代码仅供参考 private static final int OFFSET_X = 100; private static final int OFFSET_Y = 100; private static final int ADD_OFFSET_Y = 150; private static final int BUTTON_WIDTH = 800; private static final int BUTTON_HEIGHT = 100; private static final int TEXT_SIZE = 50; private int offsetY = 0; private Button createButton(String text, ShapeElement buttonBg) { Button button = new Button(this); button.setContentPosition(OFFSET_X, OFFSET_Y + offsetY); offsetY += ADD_OFFSET_Y; button.setWidth(BUTTON_WIDTH); button.setHeight(BUTTON_HEIGHT); button.setTextSize(TEXT_SIZE); button.setTextColor(Color.YELLOW); button.setText(text); button.setBackground(buttonBg); return button; } // 按照顺序在PositionLayout中依次添加按钮的示例 private void addComponents(PositionLayout linear, ShapeElement buttonBg) { // 构建显示注册流转任务管理服务的按钮 Button btnRegister = createButton("register", buttonBg); btnRegister.setClickedListener(mRegisterListener); linear.addComponent(btnRegister); // 构建显示设备列表的按钮 Button btnShowDeviceList = createButton("ShowDeviceList", buttonBg); btnShowDeviceList.setClickedListener(mShowDeviceListListener); linear.addComponent(btnShowDeviceList); // 构建跨端迁移FA的按钮 Button btnContinueRemoteFA = createButton("ContinueRemoteFA", buttonBg); btnContinueRemoteFA.setClickedListener(mContinueAbilityListener); linear.addComponent(btnContinueRemoteFA); // 构建可拉回迁移FA的按钮 Button btnContinueReversibly = createButton("ContinueReversibly", buttonBg); btnContinueReversibly.setClickedListener(mContinueReversiblyListener); linear.addComponent(btnContinueReversibly); // 构建拉回FA的按钮 Button btnReverseContinue = createButton("ReverseContinuation", buttonBg); btnReverseContinue.setClickedListener(mReverseContinueListener); linear.addComponent(btnReverseContinue); } @Override public void onStart(Intent intent) { ... //添加功能按钮布局 addComponents(layout, buttonBg); super.setUIContent(layout); }
注册流转任务管理服务。
// 注册流转任务管理服务 private Component.ClickedListener mRegisterListener = new Component.ClickedListener() { @Override public void onClick(Component arg0) { HiLog.info(LABEL_LOG, "register call."); //增加过滤条件 ExtraParams params = new ExtraParams(); String[] devTypes = new String[]{ExtraParams.DEVICETYPE_SMART_PAD, ExtraParams.DEVICETYPE_SMART_PHONE}; params.setDevType(devTypes); String jsonParams = "{'filter':{'commonFilter':{'system':{'harmonyVersion':'2.0.0'},'groupType':'1|256','curComType': 0x00030004,'faFilter':'{\"localVersionCode\":1,\"localMinCompatibleVersionCode\":2,\"targetBundleName\": \"com.xxx.yyy\"}'}},'transferScene':0,'remoteAuthenticationDescription': '拉起HiVision扫描弹框描述','remoteAuthenticationPicture':''}"; params.setJsonParams(jsonParams); continuationRegisterManager.register(BUNDLE_NAME, params, callback, requestCallback); } };
通过流转任务管理服务提供的showDeviceList()接口获取选择设备列表,用户选择设备后在IContinuationDeviceCallback回调中获取设备ID。
// 显示设备列表,获取设备信息 private ClickedListener mShowDeviceListListener = new ClickedListener() { @Override public void onClick(Component arg0) { // 设置过滤设备类型 ExtraParams params = new ExtraParams(); String[] devTypes = new String[]{ExtraParams.DEVICETYPE_SMART_PAD, ExtraParams.DEVICETYPE_SMART_PHONE}; params.setDevType(devTypes); String jsonParams = "{'filter':{'commonFilter':{'system':{'harmonyVersion':'2.0.0'},'groupType':'1|256','curComType': 0x00030004,'faFilter':'{\"localVersionCode\":1,\"localMinCompatibleVersionCode\":2,\"targetBundleName\": \"com.xxx.yyy\"}'}},'transferScene':0,'remoteAuthenticationDescription': '拉起HiVision扫描弹框描述','remoteAuthenticationPicture':''}"; params.setJsonParams(jsonParams); // 显示选择设备列表 continuationRegisterManager.showDeviceList(abilityToken, params, null); } };
可使用两种方法实现FA的迁移。
方法一:直接迁移FA,迁移后不可回迁。
方法二:迁移一个支持回迁的FA,迁移后还可将FA拉回到本端。
将运行时的FA迁移到目标设备,实现业务在设备间无缝迁移。
// 跨端迁移FA private ClickedListener mContinueAbilityListener = new ClickedListener() { @Override public void onClick(Component arg0) { if (selectDeviceId != null) { // 用户点击后发起迁移流程 continueAbility(selectDeviceId); } } };
设置一个支持回迁FA的迁移功能按钮,以及拉回该FA的功能按钮。
// 设置支持回迁FA的迁移按钮 private Component.ClickedListener mContinueReversiblyListener = new Component.ClickedListener() { @Override public void onClick(Component arg0) { if (selectDeviceId != null) { // 用户选择设备后实现可拉回迁移 continueAbilityReversibly(selectDeviceId); isReversibly = true; } } }; // 设置拉回已迁移FA的按钮 private Component.ClickedListener mReverseContinueListener = new Component.ClickedListener() { @Override public void onClick(Component arg0) { // 用户拉回迁移FA if (isReversibly) { reverseContinueAbility(); isReversibly = false; } } };
FA的迁移还涉及到状态数据的传递,需要实现IAbilityContinuation接口,供开发者实现迁移过程中特定事件的管理能力,代码示例如下:
public class MainAbilitySlice extends AbilitySlice implements IAbilityContinuation { private void showMessage(String msg) { ToastDialog toastDialog = new ToastDialog(this); toastDialog.setText(msg); toastDialog.show(); } @Override public boolean onStartContinuation() { showMessage("ContinueAbility Start"); return true; } @Override public boolean onSaveData(IntentParams saveData) { String exampleData = String.valueOf(System.currentTimeMillis()); saveData.setParam("continueParam", exampleData); return true; } @Override public boolean onRestoreData(IntentParams restoreData) { // 远端FA迁移传来的状态数据,开发者可以按照特定的场景对这些数据进行处理 Object data = restoreData.getParam("continueParam"); return true; } @Override public void onCompleteContinuation(int result) { // 开发者可以根据业务需要,提示用户迁移完成,关闭本端FA showMessage("ContinueAbility Done"); if (!isReversibly) { terminateAbility(); } } @Override public void onFailedContinuation(int errorCode) { // 开发者可以根据业务需要,提示用户迁移失败 showMessage("ContinueAbility failed"); if (!isReversibly) { terminateAbility(); } } }
通过自定义迁移事件相关的行为,最终实现对FA的迁移。此处主要以较为常用的两个事件,包括迁移发起端完成迁移的回调onCompleteContinuation(int result),以及接收到远端迁移行为传递数据的回调onRestoreData(IntentParams restoreData)。其他还包括用于本端迁移发起时保存状态数据的回调onSaveData(IntentParams saveData)和本端发起迁移的回调onStartContinuation()。按照实际应用自定义特定场景对应的回调,可以完成多种场景下FA的迁移任务。