#星光计划2.0# PageAbility跨设备迁移开发实战——问答互动 原创 精华

发布于 2021-12-16 17:26
浏览
4收藏

【本文正在参与51CTO HarmonyOS技术社区创作者激励计划-星光计划2.0】

活动链接: https://harmonyos.51cto.com/posts/9422


跨设备迁移是指将应用中的Page页迁移到另一设备中。可以同步应用数据,甚至可以在的不同设备间迁移,是HarmonyOS特色之一。于是,我以官方给了分布式邮件系统为例,写了一个简单的问答互动应用。用户在设备A上提问,在设备B上回答,信息通过迁移传递,并且能查看问答记录。

@[toc]

效果展示

#星光计划2.0# PageAbility跨设备迁移开发实战——问答互动-开源基础软件社区

主要功能

  1. 实现问答界面,通过发送按钮将问题、答题等信息转递到另一设备上。
  2. 实现问题记录界面,对每个完整的问答进行记录,方便查看。
  3. 设备间的数据进行同步,拥有相同的问答记录。

迁移的主要步骤

  1. 设备A上的Page请求迁移。
  2. HarmonyOS处理迁移任务,并回调设备A上Page的保存数据方法,用于保存迁移必须的数据。
  3. HarmonyOS在设备B上启动同一个Page,并回调其恢复数据方法。

PageAbility实现迁移是需要实现IAbilityContinuation接口的,该接口如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package ohos.aafwk.ability;

import ohos.aafwk.content.IntentParams;

public interface IAbilityContinuation {
    int ERR_ABILITY_QUERY_FAILED = -2;
    int ERR_CONTINUE_TIMEOUT = -8;
    int ERR_DEVICE_OFFLINE = -9;
    int ERR_INSTALL_FREE_NOT_SUPPORTED = -4;
    int ERR_NETWORK_UNAVAILABLE = -3;
    int ERR_PARAMETER_INVALID = -6;
    int ERR_PERMISSION_DENIED = -5;
    int ERR_REMOTE_DEVICE_INCOMPATIBLE = -7;
    int ERR_UNKNOWN = -1;
    int SUCCESS = 0;

    boolean onStartContinuation();

    boolean onSaveData(IntentParams var1);

    boolean onRestoreData(IntentParams var1);

    void onCompleteContinuation(int var1);

    default void onRemoteTerminated() {
        throw new RuntimeException("Stub!");
    }

    default void onFailedContinuation(int errorCode) {
        throw new RuntimeException("Stub!");
    }
}

除了一些异常码枚举外,都是迁移中需要用到的主要接口,onStartContinuation()是迁移开始前的预处理函数,可以在这加一些条件检测,提示等。但是在开始请求迁移前,需要申请权限ohos.permission.DISTRIBUTED_DATASYNC。config.json中的配置如下:

config.json

"reqPermissions": [
    {
        "name": "ohos.permission.DISTRIBUTED_DATASYNC"
    }
]

接下来只需要PageAbility实现Ability中的onRequestPermissionsFromUserResult接口,就能在启用迁移之前完成权限申请了。

@Override
public void onRequestPermissionsFromUserResult(int requestCode, String[] permissions, int[] grantResults) {
    if (permissions == null || permissions.length == 0 || grantResults == null || grantResults.length == 0) {
        return;
    }
    if (requestCode == 0) {
        if (grantResults[0] == IBundleManager.PERMISSION_DENIED) {
            terminateAbility();
        }
    }
}

完成权限申请后,只需要通过事件来触发迁移开关就行了。可以通过按钮的点击事件的来触发迁移开关continueAbility(),如下:

private void initComponents() {
    questionTextField = (TextField) findComponentById(ResourceTable.Id_question_content);

    answerTextField = (TextField) findComponentById(ResourceTable.Id_answer_content);

    findComponentById(ResourceTable.Id_send_button).setClickedListener(this::migrateAbility);
    findComponentById(ResourceTable.Id_return_button).setClickedListener(component->terminate());
}

private void migrateAbility(Component component) {
    String questionSend = questionTextField.getText();
    String answerSend = answerTextField.getText();
    if (questionSend.isEmpty() && answerSend.isEmpty()) {
        new ToastDialog(this).setText("Text can not be null").show();
        return;
    }

    try {
        continueAbility();
    } catch (IllegalStateException illegalStateException) {
        HiLog.error(LABEL_LOG, "%{public}s", "migrateAbility: IllegalStateException");
    }
}

最重要的两个接口莫过于onSaveDataonRestoreData了,一个是在迁移的时候,将设备A的需要输入的数据存储,另一个是在设备B进行迁移时,恢复数据。

@Override
public boolean onSaveData(IntentParams intentParams) {
    intentParams.setParam(QUESTION_KEY, questionTextField.getText());
    intentParams.setParam(ANSWER_KEY, answerTextField.getText());

    return true;
}

@Override
public boolean onRestoreData(IntentParams intentParams) {
    if (intentParams.getParam(QUESTION_KEY) instanceof String) {
        questionText = (String) intentParams.getParam(QUESTION_KEY);
    }

    if (intentParams.getParam(ANSWER_KEY) instanceof String) {
        answerText = (String) intentParams.getParam(ANSWER_KEY);
    }

    if (!questionText.isEmpty() && ! answerText.isEmpty()) {
        AskRecordSlice.UpdateContent("Q:" + questionText + "\n");
        AskRecordSlice.UpdateContent("A:" + answerText + "\n");
    }

    return true;
}

其中的IntentParams是迁移的数据包,提供了setParamgetParam,来传输Key-Value数据。

设备B上只要正常运行了onRestoreData后,那就会回调设备A上的onCompleteContinuation,表示迁移顺利完成,否则回调onFailedContinuation,通过捕捉异常码可进行异常处理。而我在正常迁移完成后,进行了问答记录的本地存储:

@Override
public void onCompleteContinuation(int code) {
    questionText = questionTextField.getText();
    answerText = answerTextField.getText();
    if (!questionText.isEmpty() && ! answerText.isEmpty()) {
        AskRecordSlice.UpdateContent("Q:" + questionText + "\n");
        AskRecordSlice.UpdateContent("A:" + answerText + "\n");
    }
}


具体代码

由于目录树中文件较多,整个工程文件的git路径为:
https://gitee.com/baboon-chen/harmony-osexample.git

需要特殊注意的点:

//1 跨不同设备时,需要在配置文件中添加上支持的设备类型 config.json
"deviceType": [
      "phone",
      "tablet"
    ],
//2 要实现接口的类有哪些?
一个应用可能包含多个Page,都有自己的PageSlice栈。仅需要在支持迁移的Page中通过以下方法实现IAbilityContinuation接口。同时,此Page所包含的所有AbilitySlice也需要实现此接口。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2021-12-16 17:26:28修改
7
收藏 4
回复
举报
回复
添加资源
添加资源将有机会获得更多曝光,你也可以直接关联已上传资源 去关联
    相关推荐