目录

  • 说些废话
  • 开源代码
  • 环境
  • 代码
  • ability_main.xml
  • config.json
  • MainAbility.java
  • MainAbilitySlice.java
  • 测试


说些废话

    目前是2022-12-13,经有关核实,HarmonyOS2.0与3.0之间的回迁是无法有效果的。(3.0与3.0因为无机器测试所以暂不知。)然后也经过有关前辈报给华为那边了~

    官方文档:跨端迁移开发指导(基于java开发)     下面环境里写的两台真机我测试的时候无法正常产生回迁效果,所以实际上我是用的远程模拟器操作成功的,但是我懒得删我的设备信息了。

    模拟器不需要登录华为账号,但是需要在 设置-超级终端-允许被发现 处打开能够被附近设备发现。

Android app代码 命令行转为鸿蒙app代码 安卓代码迁移鸿蒙_java

开源代码

    CrossSiteMigration

环境

    HUAWEI MatePad Pro 10.8英寸 2019款 HarmonyOS3.0
    Mate 10 Pro 全网通版 HarmonyOS2.0
    DevEco Studio 3.0.0.993 Release
    SDK 6(使用java)
    我看的《公共事件开发指导》更新于2022-10-28 18:02

代码

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">

    <Text
        ohos:id="$+id:text_register"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:layout_alignment="horizontal_center"
        ohos:text="注册流转服务"
        ohos:text_size="40vp"
        />
    <Text
        ohos:id="$+id:text_show"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:layout_alignment="horizontal_center"
        ohos:text="列出可流转的设备"
        ohos:scrollable="true"
        ohos:text_size="40vp"
        />
    <Text
        ohos:id="$+id:text_startMigration"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:layout_alignment="horizontal_center"
        ohos:text="开始迁移"
        ohos:scrollable="true"
        ohos:text_size="40vp"
        />
    <Text
        ohos:id="$+id:text_showParams"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:layout_alignment="horizontal_center"
        ohos:text="这里展示数据:无"
        ohos:scrollable="true"
        ohos:text_size="40vp"
        />
    <Text
        ohos:id="$+id:text_returnMigration"
        ohos:height="match_content"
        ohos:width="match_content"
        ohos:layout_alignment="horizontal_center"
        ohos:text="迁移回去"
        ohos:scrollable="true"
        ohos:text_size="40vp"
        />
</DirectionalLayout>

config.json

    长度问题只放module部分。

"module": {
    "package": "com.openvalley.cyj.migration",
    "name": ".MyApplication",
    "mainAbility": "com.openvalley.cyj.migration.MainAbility",
    "deviceType": [
      "phone",
      "tablet"
    ],
    "distro": {
      "deliveryWithInstall": true,
      "moduleName": "entry",
      "moduleType": "entry",
      "installationFree": false
    },
    "abilities": [
      {
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ],
        "name": "com.openvalley.cyj.migration.MainAbility",
        "description": "$string:mainability_description",
        "icon": "$media:icon",
        "label": "$string:entry_MainAbility",
        "launchType": "standard",
        "orientation": "unspecified",
        "visible": true,
        "type": "page"
      }
    ],
    "reqPermissions": [
      {
        "name": "ohos.permission.DISTRIBUTED_DATASYNC"
      }
    ]
  }

MainAbility.java

package com.openvalley.cyj.migration;

import com.openvalley.cyj.migration.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.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.security.SystemPermission;

public class MainAbility extends Ability implements IAbilityContinuation {
    private static final HiLogLabel LABEL_LOG = new HiLogLabel(HiLog.LOG_APP, 0x00001, MainAbility.class.getSimpleName());

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setMainRoute(MainAbilitySlice.class.getName());

        verifyAndRequestSelfPermission();
    }

    /**
     * 在此处验证和请求用户权限
     */
    private void verifyAndRequestSelfPermission() {
        try{
            //返回值0有,-1没有
            if(verifyCallingOrSelfPermission(SystemPermission.DISTRIBUTED_DATASYNC) == IBundleManager.PERMISSION_GRANTED){
//            if(verifySelfPermission(SystemPermission.LOCATION) == IBundleManager.PERMISSION_GRANTED){
                //如果有权限
            }else {
                //如果没授权就看这个权限能否动态授权
                if(canRequestPermission(SystemPermission.DISTRIBUTED_DATASYNC)){
                    //如果可以弹窗授权
                    requestPermissionsFromUser(new String[] {SystemPermission.DISTRIBUTED_DATASYNC}, 0x10001);
                }else {
                    //如果不能动态授权
                    return ;
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 授权的回调
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsFromUserResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsFromUserResult(requestCode, permissions, grantResults);
        if(requestCode == 0x10001){
            if(grantResults[0] == IBundleManager.PERMISSION_GRANTED){
                //如果授权成功(0)
                HiLog.info(LABEL_LOG, permissions[0] + "权限授权成功。");
            }else {
                //如果没有授权成功(-1)
                return ;
            }
        }
    }

    /**
     * 准备开始迁移的回调
     * @return
     */
    @Override
    public boolean onStartContinuation() {
        //返回值为true才发起迁移
        HiLog.info(LABEL_LOG, "MainAbility onStartContinuation");
        return true;
    }

    /**
     * 当保存数据(要迁移的数据)的回调
     * @param intentParams
     * @return
     */
    @Override
    public boolean onSaveData(IntentParams intentParams) {
        HiLog.info(LABEL_LOG, "MainAbility onSaveData");
        return true;
    }

    /**
     * 当恢复数据的回调(应该是被迁移的设备调用
     * @param intentParams
     * @return
     */
    @Override
    public boolean onRestoreData(IntentParams intentParams) {
        //返回为true代表恢复数据成功
        HiLog.info(LABEL_LOG, "MainAbility onRestoreData");
        return true;
    }

    /**
     * 迁移完成的回调
     * @param i
     */
    @Override
    public void onCompleteContinuation(int i) {
        HiLog.info(LABEL_LOG, " MainAbility onCompleteContinuation");
    }

    /**
     * 迁移过程中失败的回调
     */
    @Override
    public void onRemoteTerminated() {
        IAbilityContinuation.super.onRemoteTerminated();
        HiLog.info(LABEL_LOG, "MainAbility onRemoteTerminated");
    }

    /**
     * 回迁允许的情况下,远端中断
     * @param errorCode
     */
    @Override
    public void onFailedContinuation(int errorCode) {
        IAbilityContinuation.super.onFailedContinuation(errorCode);
        HiLog.info(LABEL_LOG, "MainAbility onFailedContinuation");
    }
}

MainAbilitySlice.java

package com.openvalley.cyj.migration.slice;

import com.openvalley.cyj.migration.ResourceTable;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.ability.IAbilityContinuation;
import ohos.aafwk.ability.continuation.*;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.IntentParams;
import ohos.agp.components.Text;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

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

    Text text_register = null;
    Text text_show = null;
    Text text_startMigration = null;
    Text text_showParams = null;
    Text text_returnMigration = null;

    IContinuationRegisterManager continuationRegisterManager = null;
    IContinuationDeviceCallback continuationDeviceCallback = null;
    RequestCallback requestCallback = null;
    String jsonParams = null;
    int abilityToken = 0;
    String deviceId = null;

    String params = null;
    boolean isMigration = false;

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

        initContinuationRegisterManager();
        initComponent();
    }

    /**
     * 初始化注册管理类
     */
    private void initContinuationRegisterManager(){
       continuationRegisterManager = getContinuationRegisterManager();
       //设备的回调(是在列出了设备列表后,选择某个设备进行流转连接成功或者断开时调用)
       continuationDeviceCallback = new IContinuationDeviceCallback() {
           @Override
           public void onConnected(ContinuationDeviceInfo deviceInfo) {
               HiLog.info(LABEL_LOG, "onConnected()");
               deviceId = deviceInfo.getDeviceId();
               HiLog.info(LABEL_LOG, "onConnected() 连接成功。 deviceId = " + deviceId);
               continuationRegisterManager.updateConnectStatus(abilityToken, deviceId, DeviceConnectState.CONNECTED.getState(), requestCallback);
           }

           @Override
           public void onDeviceConnectDone(String s, String s1) {
               HiLog.info(LABEL_LOG, "onDeviceConnectDone()");
           }

           @Override
           public void onDisconnected(String deviceId) {
               HiLog.info(LABEL_LOG, "onDisconnected()");
           }

           @Override
           public void onDeviceDisconnectDone(String s) {
               HiLog.info(LABEL_LOG, "onDeviceDisconnectDone()");
           }
       };
       //请求的回调(注册到服务中心成功)
        requestCallback = new RequestCallback() {
            @Override
            public void onResult(int i) {
                if(i > 0){
                    //为了保证更新数据时这个值还在,所以在if里写
                    abilityToken = i;
                    HiLog.info(LABEL_LOG, "onResult() 注册成功。 abilityToken = " + abilityToken);
                }else {
                    HiLog.info(LABEL_LOG, "onResult() 其他操作。 abilityToken = " + i);
                }
            }
        };
    }

    /**
     * 初始化组件
     */
    private void initComponent(){
        text_register = (Text)findComponentById(ResourceTable.Id_text_register);
        text_register.setClickedListener(listen -> register());

        text_show = (Text)findComponentById(ResourceTable.Id_text_show);
        text_show.setClickedListener(listen -> show());

        text_startMigration = (Text)findComponentById(ResourceTable.Id_text_startMigration);
        text_startMigration.setClickedListener(listen -> startMigration());

        text_showParams = (Text)findComponentById(ResourceTable.Id_text_showParams);
        text_showParams.setText("这里展示数据:" + params);

        text_returnMigration = (Text)findComponentById(ResourceTable.Id_text_returnMigration);
        text_returnMigration.setClickedListener(listen -> returnMigration());
    }

    /**
     * 点击按钮进行流转服务注册
     */
    private void register(){
        if(continuationRegisterManager != null){
            HiLog.info(LABEL_LOG, "register() 开始注册。");
            continuationRegisterManager.register(getBundleName(), getExtraParams(), continuationDeviceCallback, requestCallback);
        }
    }

    /**
     * 展示可流转的设备
     */
    private void show(){
        if(continuationRegisterManager != null && abilityToken > 0){
            continuationRegisterManager.showDeviceList(abilityToken, getExtraParams(), requestCallback);
        }
    }

    /**
     * 开始迁移
     */
    private void startMigration(){
        if(continuationRegisterManager != null && deviceId != null){
            HiLog.info(LABEL_LOG, "startMigration() 开始迁移。");
//            continueAbility(deviceId);
            continueAbilityReversibly(deviceId);
            isMigration = true;
            HiLog.info(LABEL_LOG, "startMigration() 迁移结束。");
            //如果是不回迁的,这边的页面就可以销毁掉
//            terminateAbility();
        }
    }

    /**
     * 迁移回原设备
     */
    private void returnMigration(){
//        ContinuationState.
        try{
            boolean reverseContinueAbility = reverseContinueAbility();
            HiLog.info(LABEL_LOG, "reverseContinueAbility = " + reverseContinueAbility);
            isMigration = false;
        }catch (Exception e){
            HiLog.info(LABEL_LOG, "e = " + e.getMessage());
        }
    }

    /**
     * 获得注册时 额外的一些参数
     * @return
     */
    private ExtraParams getExtraParams(){
        ExtraParams extraParams = new ExtraParams();
        String[] devType = {ExtraParams.DEVICETYPE_SMART_PHONE, ExtraParams.DEVICETYPE_SMART_PAD};
        //显示的列表包含那些设备类型
        extraParams.setDevType(devType);
        //添加描述(没出来)
        extraParams.setDescription("这是一个描述。");
        //如果不写,就会默认以注册时的BundleName作为targetBundleName
        extraParams.setTargetBundleName(getBundleName());
        //设备过滤
        jsonParams = "\"{" +
                        "\"filter\":{" +
                            "\"commonFilter\":{" +
                                "\"system\":{" +
                                    //目标设备对应的HarmonyOS版本 要≥
                                    "\"harmonyVersion\":\"2.0.0\"" +
                                "}," +
//                                "}" +
                                //1为同账号,1|256为不同账号
                                "\"groupType\":\"1|256\"," +
                                //0x00000004需要在同一局域网下,0x00030004不需要在同一局域网下
                                "\"curComType\": 0x00030004," +
                                //为空就不做版本兼容性检查,要做版本兼容性检查时,需要传入目标应用包名。
                                "\"faFilter\":\"{" +
                                    "\"targetBundleName\":\"com.openvalley.cyj.migration\"" +
                                "}\"" +
                            "}" +
                        "}," +
                        "\"transferScene\":0," +
                        "\"remoteAuthenticationDescription\": \"拉起HiVision扫描弹框描述\"" +
                    "}\"";
//        HiLog.info(LABEL_LOG, jsonParams);
        extraParams.setJsonParams(jsonParams);
        return extraParams;
    }

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

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

    @Override
    protected void onStop() {
        if(continuationRegisterManager != null && abilityToken > 0){
            continuationRegisterManager.unregister(abilityToken, requestCallback);
        }
    }

    /**
     * 准备开始迁移的回调
     * @return
     */
    @Override
    public boolean onStartContinuation() {
        //返回值为true才发起迁移
        HiLog.info(LABEL_LOG, "MainAbilitySlice onStartContinuation");
        return true;
    }

    /**
     * 当保存数据(要迁移的数据)的回调
     * @param intentParams
     * @return
     */
    @Override
    public boolean onSaveData(IntentParams intentParams) {
        HiLog.info(LABEL_LOG, "MainAbilitySlice onSaveData");
        intentParams.setParam("userName", "陈依劼");
        return true;
    }

    /**
     * 当恢复数据的回调(应该是被迁移的设备调用
     * @param intentParams
     * @return
     */
    @Override
    public boolean onRestoreData(IntentParams intentParams) {
        //返回为true代表恢复数据成功
        HiLog.info(LABEL_LOG, "MainAbilitySlice onRestoreData");
        Object userName = intentParams.getParam("userName");
        if(userName != null){
            params = (String)userName;
        }
        return true;
    }

    /**
     * 迁移完成的回调
     * @param i
     */
    @Override
    public void onCompleteContinuation(int i) {
        HiLog.info(LABEL_LOG, "MainAbilitySlice onCompleteContinuation");
    }

    /**
     * 迁移过程中失败的回调
     */
    @Override
    public void onRemoteTerminated() {
        HiLog.info(LABEL_LOG, "MainAbilitySlice onRemoteTerminated");
    }

    /**
     * 回迁允许的情况下,远端中断
     * @param errorCode
     */
    @Override
    public void onFailedContinuation(int errorCode) {
        IAbilityContinuation.super.onFailedContinuation(errorCode);
        HiLog.info(LABEL_LOG, "MainAbilitySlice onFailedContinuation");
    }
}

测试

    注意要先把想迁移到的设备的上的程序先授权。