PageAbility跨設(shè)備遷移開發(fā)實(shí)戰(zhàn)—問答互動
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
跨設(shè)備遷移是指將應(yīng)用中的Page頁遷移到另一設(shè)備中??梢酝綉?yīng)用數(shù)據(jù),甚至可以在的不同設(shè)備間遷移,是HarmonyOS特色之一。于是,我以官方給了分布式郵件系統(tǒng)為例,寫了一個簡單的問答互動應(yīng)用。用戶在設(shè)備A上提問,在設(shè)備B上回答,信息通過遷移傳遞,并且能查看問答記錄。
Table of Contents
效果展示

主要功能
實(shí)現(xiàn)問答界面,通過發(fā)送按鈕將問題、答題等信息轉(zhuǎn)遞到另一設(shè)備上。
實(shí)現(xiàn)問題記錄界面,對每個完整的問答進(jìn)行記錄,方便查看。
設(shè)備間的數(shù)據(jù)進(jìn)行同步,擁有相同的問答記錄。
遷移的主要步驟
- 設(shè)備A上的Page請求遷移。
- HarmonyOS處理遷移任務(wù),并回調(diào)設(shè)備A上Page的保存數(shù)據(jù)方法,用于保存遷移必須的數(shù)據(jù)。
- HarmonyOS在設(shè)備B上啟動同一個Page,并回調(diào)其恢復(fù)數(shù)據(jù)方法。
PageAbility實(shí)現(xiàn)遷移是需要實(shí)現(xiàn)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()是遷移開始前的預(yù)處理函數(shù),可以在這加一些條件檢測,提示等。但是在開始請求遷移前,需要申請權(quán)限ohos.permission.DISTRIBUTED_DATASYNC。config.json中的配置如下:
config.json
- "reqPermissions": [
- {
- "name": "ohos.permission.DISTRIBUTED_DATASYNC"
- }
- ]
接下來只需要PageAbility實(shí)現(xiàn)Ability中的onRequestPermissionsFromUserResult接口,就能在啟用遷移之前完成權(quán)限申請了。
- @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();
- }
- }
- }
完成權(quán)限申請后,只需要通過事件來觸發(fā)遷移開關(guān)就行了??梢酝ㄟ^按鈕的點(diǎn)擊事件的來觸發(fā)遷移開關(guān)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了,一個是在遷移的時候,將設(shè)備A的需要輸入的數(shù)據(jù)存儲,另一個是在設(shè)備B進(jìn)行遷移時,恢復(fù)數(shù)據(jù)。
- @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是遷移的數(shù)據(jù)包,提供了setParam、getParam,來傳輸Key-Value數(shù)據(jù)。
設(shè)備B上只要正常運(yùn)行了onRestoreData后,那就會回調(diào)設(shè)備A上的onCompleteContinuation,表示遷移順利完成,否則回調(diào)onFailedContinuation,通過捕捉異常碼可進(jìn)行異常處理。而我在正常遷移完成后,進(jìn)行了問答記錄的本地存儲:
- @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
需要特殊注意的點(diǎn):
- //1 跨不同設(shè)備時,需要在配置文件中添加上支持的設(shè)備類型 config.json
- "deviceType": [
- "phone",
- "tablet"
- ],
- //2 要實(shí)現(xiàn)接口的類有哪些?
- 一個應(yīng)用可能包含多個Page,都有自己的PageSlice棧。僅需要在支持遷移的Page中通過以下方法實(shí)現(xiàn)IAbilityContinuation接口。同時,此Page所包含的所有AbilitySlice也需要實(shí)現(xiàn)此接口。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)