#星光计划2.0# PageAbility跨设备迁移开发实战——问答互动 原创 精华
【本文正在参与51CTO HarmonyOS技术社区创作者激励计划-星光计划2.0】
活动链接: https://harmonyos.51cto.com/posts/9422
跨设备迁移是指将应用中的Page页迁移到另一设备中。可以同步应用数据,甚至可以在的不同设备间迁移,是HarmonyOS特色之一。于是,我以官方给了分布式邮件系统为例,写了一个简单的问答互动应用。用户在设备A上提问,在设备B上回答,信息通过迁移传递,并且能查看问答记录。
@[toc]
效果展示
主要功能
- 实现问答界面,通过发送按钮将问题、答题等信息转递到另一设备上。
- 实现问题记录界面,对每个完整的问答进行记录,方便查看。
- 设备间的数据进行同步,拥有相同的问答记录。
迁移的主要步骤
- 设备A上的Page请求迁移。
- HarmonyOS处理迁移任务,并回调设备A上Page的保存数据方法,用于保存迁移必须的数据。
- 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");
}
}
最重要的两个接口莫过于onSaveData
、onRestoreData
了,一个是在迁移的时候,将设备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
是迁移的数据包,提供了setParam
、getParam
,来传输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也需要实现此接口。
点开之前以为是在线问答贴