HarmonyOS流轉之跨端遷移
前言
流轉在HarmonyOS中泛指多設備分布式操作,也是HarmonyOS的亮點之一。流轉按體驗可以分為跨端遷移和多端協同,這里主要跟大家講一下如何進行跨端遷移,以及我在項目開發過程中,所遇到的問題與解決方法。
具體概念這里就不做過多的贅述了,大家可以查閱官方文檔。
開發步驟
在開發過程中,我們可以根據業務需求分為以下兩種場景:
- 同個FA之間的遷移(Ability1—Ability1);
- 不同FA之間的遷移(Ability1—Ability2)。
下面給大家介紹一下以上兩種場景的具體的開發步驟。
同個FA之間的遷移
同個FA之間的遷移是指不同設備端安裝了同個FA,官方文檔已經有比較詳細的開發步驟,下面只給大家講一下需要注意的事項及我所遇到的問題避免大家踩坑。
1.我們在創建完一個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;
- }
- //省略部分代碼
- ...
- }
2.在對應的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);
- }
3.定義相關參數、設置流轉任務管理服務回調函數、注冊流轉任務管理服務、管理流轉的目標設備,同時需要在流轉結束時解注冊流轉任務管理服務。
- // 流轉應用包名
- 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及設備類型。
4.注冊流轉服務之后我們便可以調起系統流轉選擇設備彈窗,可以通過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, params, new RequestCallback() {
- @Override
- public void onResult(int result) {
- }
- });
5.選擇完設備之后會通過上述的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();
- }
- };
6.在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,具體實現如下。
1.首先,我們先在選擇設備成功后的回調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();
- }
- ...
- }
- ....
- };
2.之前,我們是通過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)方法接受傳遞過來的參數,再根據自己的業務邏輯實現數據同步。
自定義設備選擇彈窗
在實際項目開發中我們也可以自定義流轉彈窗樣式,但并不推薦這種方式,經測試發現只有在兩個設備通過藍牙連接的時候才能獲取到設備列表,只有在特定的場景,例如手機與車機、手機與手表在實際使用過程中我們基本上是會保持藍牙連接的,通過這種方式實現流轉會更穩定。但如果不能保持藍牙實時連接的場景則不推薦。
1.官方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;
- }
2.獲取到設備列表后,我們就可以自行實現頁面了,在上述的showDeviceList()彈出設備列表的位置替換成自己的彈窗即可。
效果展示

結語
目前在DevEco Studio 2.1 Release以上版本已經支持跨端遷移的模擬器了,如果沒有顯示出來可以在Settings-DevEco Labs 勾選Enable Super Device。
文章相關附件可以點擊下面的原文鏈接前往下載:
https://harmonyos.51cto.com/resource/1426


























