HarmonyOS 鸿蒙Next实现跨设备迁移与回迁

发布于 1周前 作者 sinazl 来自 鸿蒙OS

HarmonyOS 鸿蒙Next实现跨设备迁移与回迁 跨设备迁移支持将Page在同一用户的不同设备间进行迁移,以便支持用户无缝切换的诉求。以Page从设备A迁移到设备B为例,迁移动作主要步骤如下:

  1. 设备A上的Page请求迁移。
  2. HarmonyOS处理迁移任务,并回调设备A上Page的保存数据方法,用于保存迁移必须的数据。
  3. HarmonyOS在设备B上启动同一个Page,并回调其恢复数据方法。
  4. 请求迁移并完成后,设备A上已迁移的Page可以发起回迁,以便使用户活动重新回到此设备。

本节演示如何实现跨设备迁移与回迁。

选择模板

选择Empty Ablity(Java)

创建项目

创建名为ContinueRemoteFA的项目作为演示

项目创建完成之后,会自动生成基础的代码结构,接下来直接在该结构上进行微调即可。

MainAbility实现implements IAbilityContinuation接口

MainAbility实现implements IAbilityContinuation接口,代码如下:

package com.waylau.hmos.continueremotefa;

import com.waylau.hmos.continueremotefa.slice.MainAbilitySlice;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.IAbilityContinuation;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.IntentParams;
import ohos.bundle.IBundleManager;
import ohos.security.SystemPermission;
import java.util.ArrayList;
import java.util.List;

/**
 * 设备迁移与回迁
 *
 * @author <a href="https://waylau.com">Way Lau</a>
 */
public class MainAbility extends Ability implements IAbilityContinuation {
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setMainRoute(MainAbilitySlice.class.getName());
        requestPermission();
    }

    //获取权限
    private void requestPermission() {
        String[] permission = {
                SystemPermission.DISTRIBUTED_DATASYNC
        };
        List<String> applyPermissions = new ArrayList<>();
        for (String element : permission) {
            if (verifySelfPermission(element) != 0) {
                if (canRequestPermission(element)) {
                    applyPermissions.add(element);
                }
            }
        }
        requestPermissionsFromUser(applyPermissions.toArray(new String[0]), 0);
    }

    @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) {

    }
}

上述代码,重点是重写onStartContinuationonSaveDataonRestoreData等方法。

申明权限

在项目的对应的config.json中声明跨端迁移访问的权限:ohos.permission.DISTRIBUTED_DATASYNC以及获取分布式设备信息的相关的权限。在config.json中的配置如下:

{
  "module": {
    "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,显示申明ohos.permission.DISTRIBUTED_DATASYNC权限,最终,MainAbility完整代码如下:

package com.waylau.hmos.continueremotefa;

import com.waylau.hmos.continueremotefa.slice.MainAbilitySlice;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.ability.IAbilityContinuation;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.IntentParams;
import ohos.bundle.IBundleManager;
import ohos.security.SystemPermission;
import java.util.ArrayList;
import java.util.List;

/**
 * 设备迁移与回迁
 *
 * @author <a href="https://waylau.com">Way Lau</a>
 */
public class MainAbility extends Ability implements IAbilityContinuation {
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setMainRoute(MainAbilitySlice.class.getName());
        requestPermission();
    }

    //获取权限
    private void requestPermission() {
        String[] permission = {
                SystemPermission.DISTRIBUTED_DATASYNC
        };
        List<String> applyPermissions = new ArrayList<>();
        for (String element : permission) {
            if (verifySelfPermission(element) != 0) {
                if (canRequestPermission(element)) {
                    applyPermissions.add(element);
                }
            }
        }
        requestPermissionsFromUser(applyPermissions.toArray(new String[0]), 0);
    }

    @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) {

    }
}

设计界面与布局

修改ability_main.xml布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:alignment="center"
    ohos:orientation="vertical">

    <Button
        ohos:id="$+id:button_continue_remote_fa"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:background_element="$graphic:background_ability_main"
        ohos:layout_alignment="horizontal_center"
        ohos:text="迁移"
        ohos:text_size="40vp"/>

    <Button
        ohos:id="$+id:button_continue_eversibly"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:top_margin="8vp"
        ohos:background_element="$graphic:background_ability_main"
        ohos:layout_alignment="horizontal_center"
        ohos:text="回迁"
        ohos:text_size="40vp"/>

</DirectionalLayout>

同时,修改background_ability_main.xml样式文件如下:

<?xml version="1.0" encoding="UTF-8" ?>
<shape xmlns:ohos="http://schemas.huawei.com/res/ohos"
       ohos:shape="rectangle">
    <corners
        ohos:radius="10"/>
    <solid
        ohos:color="#FAEBD7"/>
</shape>

修改MainAbilitySlice

核心的逻辑都集中到了MainAbilitySlice中。MainAbilitySlice同样也要实现implements IAbilityContinuation接口,代码如下:

public class MainAbilitySlice extends AbilitySlice implements IAbilityContinuation {
    private static final String TAG = MainAbilitySlice.class.getSimpleName();
    private static final HiLogLabel LABEL_LOG =
        new HiLogLabel(HiLog.LOG_APP, 0x00001, TAG);

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
    }

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

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }

    @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) {

    }
}

上述代码,重点是重写onStartContinuationonSaveDataonRestoreData等方法。

接着为“迁移”“回迁”按钮设置点击事件,代码如下:

@Override
public void onStart(Intent intent) {
    super.onStart(intent);
    super.setUIContent(ResourceTable.Layout_ability_main);

    // 监听跨端迁移FA的事件
    Button buttonContinueRemoteFA = (
        Button) findComponentById(ResourceTable.Id_button_continue_remote_fa);
    buttonContinueRemoteFA.setClickedListener(listener -> continueRemoteFA());

    // 监听拉回迁移FA的事件
    Button buttonContinueEversibly =
        (Button) findComponentById(ResourceTable.Id_button_continue_eversibly);
    buttonContinueEversibly.setClickedListener(listener -> continueEversibly());
}

private void continueRemoteFA() {
    HiLog.info(LABEL_LOG, "before startRemoteFA");
    String deviceId = DeviceUtils.getDeviceId();
    HiLog.info(LABEL_LOG, "get deviceId: %{public}s", deviceId);

    if (deviceId != null) {
        // 发起迁移流程
        // continueAbility()是不可回迁的
        // continueAbilityReversibly() 是可以回迁的
        continueAbilityReversibly(deviceId);
    }
}

private void continueEversibly() {
    // 发起回迁流程
    reverseContinueAbility();
}

上述代码:

  • 可以通过continueAbility 或者continueAbilityReversibly方法来执行迁移。需要注意的是continueAbility()是不可回迁的,而 continueAbilityReversibly() 是可以回迁的,因此本例子是使用了continueAbilityReversibly方法。
  • continueAbilityReversibly方法可以指定待迁移的设备的ID,该ID通过DeviceUtils工具类获取
  • 回迁调用reverseContinueAbility方法。

DeviceUtils

DeviceUtils代码如下:

package com.waylau.hmos.continueremotefa;

import ohos.distributedschedule.interwork.DeviceInfo;
import ohos.distributedschedule.interwork.DeviceManager;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import java.util.ArrayList;
import java.util.List;

/**
 * 获取当前组网下可迁移的设备id列表
 *
 * @author <a href="https://waylau.com">Way Lau</a>
 */
public class DeviceUtils {
    private static final String TAG = DeviceUtils.class.getSimpleName();
    private static final HiLogLabel LABEL_LOG =
        new HiLogLabel(HiLog.LOG_APP, 0x00001, TAG);

    private DeviceUtils() {}

    // 获取当前组网下可迁移的设备id列表
    public static List<String> getAvailableDeviceId() {
        List<String> deviceIds = new ArrayList<>();
        List<DeviceInfo> deviceInfoList =
            DeviceManager.getDeviceList(DeviceInfo.FLAG_GET_ALL_DEVICE);

        if (deviceInfoList == null) {
            return deviceIds;
        }

        if (deviceInfoList.size() == 0) {
            HiLog.warn(LABEL_LOG, "did not find other device");
            return deviceIds;
        }

        for (DeviceInfo deviceInfo : deviceInfoList) {
            deviceIds.add(deviceInfo.getDeviceId());
        }

        return deviceIds;
    }

    // 获取当前组网下可迁移的设备id
    // 如果有多个则取第一个
    public static String getDeviceId() {
        String deviceId = "";
        List<String> outerDevices = DeviceUtils.getAvailableDeviceId();

        if (outerDevices == null || outerDevices.size() == 0) {
            HiLog.warn(LABEL_LOG, "did not find other device");
        } else {
            for (String item : outerDevices) {
                HiLog.info(LABEL_LOG, "outerDevices:%{public}s", item);
            }
            deviceId = outerDevices.get(0);
        }
        HiLog.info(LABEL_LOG, "getDeviceId:%{public}s", deviceId);

        return deviceId;
    }
}

DeviceUtilsgetDeviceId方法用于获取在线设备列表。如果有多个,则取任意一个。

运行

为了演示多个设备的迁移情况,需要启动“Super device”,同时启动手机和平台两个模拟器。

将项目同时在这两个模拟器里面运行。

此时,假设手机为设备A,作为源设备,平台为设备B,作为目标设备。

在设备A上,点击“迁移”按钮,此时可以看到,Page已经已经迁移到了设备B上了。说明迁移成功了。

在设备A上,点击“回迁”按钮,此时可以看到,Page从设备B上消失了。说明回迁移成功了。


更多关于HarmonyOS 鸿蒙Next实现跨设备迁移与回迁的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

8 回复

呜呜呜 咋都是java。没有js ts实现的么?

更多关于HarmonyOS 鸿蒙Next实现跨设备迁移与回迁的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


萝卜青菜各有所爱。哈哈~,

根我想的迁移不一样,我想的是设备B上没有这个应该,我在设备A上点迁移,选设备B,就安装到B了

我们先来看下官方对于“迁移”的定义。

跨端迁移:一种实现用户应用程序流转的技术方案,指在A端运行的FA迁移到B端上,完成迁移后, B端FA继续任务,而A端应用退出。这里就几个注意点。

只能迁移FA,不能迁移PA。也就说,迁移只能迁移应用的部分。这个你可以简单理解为是一个电脑接了双个显示器,你把窗口从显示器A拖动到了显示器B,但本质上PA的运算还是在原来的那个电脑上。

迁移的是数据,并不是屏幕的镜像。这里跟显示器拖动有一个非常大的区别,就是她迁移的并不是界面本身,而是界面内部的状态数据。就好比你和你的队友在打网络游戏,你能看到你队友在移动,实际上是队友的位置坐标数据发到了你的应用,在你的应用渲染出来所看到的位置,而不是队友的位置画面同步到了你的应用。因此,是需要你和你的队友电脑都要有相同的应用才能互相识别到位置数据。

原子化服务支持免安装使用简化了迁移。有一种方式可能符合你的述求,那就是原子化服务。因为,原子化服务是支持免安装使用(并不是真正的免安装,只是体积小可以简化安装的等待过程)。因此,可以在迁移的时候再安装原子化服务,以实现两端的应用都相同的目的。

恩,我觉得这种也是可以实现的,把hap包传过去就行了,

厉害了,谢楼主分享干货

多谢支持!!!!!!!!

HarmonyOS 鸿蒙Next实现跨设备迁移与回迁的核心功能主要依赖于其分布式技术框架。该框架允许不同设备在底层实现互联互通,从而支持数据和应用的无缝迁移。

在跨设备迁移过程中,鸿蒙系统能够识别并连接目标设备,随后将当前设备上的关键数据(如用户设置、应用状态等)进行打包并安全传输至目标设备。这一过程确保了数据的完整性和隐私保护,用户无需担心数据丢失或泄露。

回迁操作则是迁移的逆过程。当用户需要将数据和应用从目标设备迁回原设备时,鸿蒙系统同样能够高效地完成这一任务。它会自动识别原设备,并将数据和应用状态恢复如初,确保用户体验的一致性。

值得注意的是,鸿蒙系统在实现跨设备迁移与回迁时,充分考虑了设备的兼容性和性能差异。它能够对不同设备的硬件和软件环境进行智能适配,确保迁移过程的顺畅和高效。

此外,鸿蒙系统还提供了丰富的API和开发工具,供开发者在实现自定义迁移逻辑时使用。这进一步扩展了跨设备迁移与回迁的应用场景和灵活性。

如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html

回到顶部