概述
在用戶使用設(shè)備的過程中真朗,當使用情境發(fā)生變化時(例如從室內(nèi)走到戶外或者周圍有更適合的設(shè)備等)萄传,之前使用的設(shè)備可能已經(jīng)不適合繼續(xù)當前的任務,此時蜜猾,用戶可以選擇新的設(shè)備來繼續(xù)當前的任務,原設(shè)備可按需決定是否退出任務振诬,這個就是跨端遷移的場景蹭睡。常見的跨端遷移場景實例:在平板上播放的視頻,遷移到智慧屏繼續(xù)播放赶么,從而獲得更佳的觀看體驗肩豁;平板上的視頻應用退出。在應用開發(fā)層面辫呻,跨端遷移指在A端運行的UIAbility遷移到B端上清钥,完成遷移后,B端UIAbility繼續(xù)任務放闺,而A端UIAbility可按需決定是否退出祟昭。
跨端遷移的核心任務是將應用的當前狀態(tài)(包括頁面控件、狀態(tài)變量等)無縫遷移到另一設(shè)備怖侦,從而在新設(shè)備上無縫接續(xù)應用體驗篡悟。這意味著用戶在一臺設(shè)備上進行的操作可以在另一臺設(shè)備的相同應用中快速切換并無縫銜接。
主要功能包括:
支持用戶自定義數(shù)據(jù)存儲及恢復匾寝。
支持頁面路由信息和頁面控件狀態(tài)數(shù)據(jù)的存儲及恢復搬葬。
支持應用兼容性檢測。
支持應用根據(jù)實際使用場景動態(tài)設(shè)置遷移狀態(tài)(默認遷移狀態(tài)為 ACTIVE 激活狀態(tài))艳悔。例如急凰,編輯類應用在編輯文本的頁面下才需要遷移,其他頁面不需要遷移猜年,則可以通過
setMissionContinueState
進行控制抡锈。支持應用動態(tài)選擇是否進行頁面椉踩蹋恢復(默認進行頁面棧信息恢復)。例如企孩,應用希望自定義遷移到其他設(shè)備后顯示的頁面锭碳,則可以通過
SUPPORT_CONTINUE_PAGE_STACK_KEY
進行控制。支持應用動態(tài)選擇遷移成功后是否退出遷移源端應用(默認遷移成功后退出遷移源端應用)勿璃∏芘祝可以通過
SUPPORT_CONTINUE_SOURCE_EXIT_KEY
進行控制。
說明:
開發(fā)者可以開發(fā)具有遷移能力的應用补疑,遷移的觸發(fā)由系統(tǒng)應用完成歧沪。
運作機制
- 在源端,通過
UIAbility
的onContinue()
回調(diào)莲组,開發(fā)者可以保存待接續(xù)的業(yè)務數(shù)據(jù)诊胞。例如,在瀏覽器應用中完成跨端遷移锹杈,開發(fā)者需要使用onContinue()
回調(diào)保存頁面URL等業(yè)務內(nèi)容撵孤,而系統(tǒng)將自動保存頁面狀態(tài),如當前瀏覽進度竭望。 - 分布式框架提供了跨設(shè)備應用界面邪码、頁面棧以及業(yè)務數(shù)據(jù)的保存和恢復機制,它負責將數(shù)據(jù)從源端發(fā)送到對端咬清。
- 在對端闭专,同一
UIAbility
可以通過onCreate()
(冷啟動)和onNewWant()
(熱啟動)接口來恢復業(yè)務數(shù)據(jù)。
約束限制
- 跨端遷移要求在同一
UIAbility
之間進行旧烧,也就是需要相同的bundleName
影钉、abilityName
和簽名信息。 - 為了獲得最佳體驗掘剪,使用
wantParam
傳輸?shù)臄?shù)據(jù)需要控制在100KB以下平委。
開發(fā)步驟
- 在 module.json5配置文件 的abilities標簽中配置跨端遷移標簽
continuable
。
{
"module": {
// ...
"abilities": [
{
// ...
"continuable": true, // 配置UIAbility支持遷移
}
]
}
}
說明:
根據(jù)需要配置應用啟動模式類型夺谁,配置詳情請參照 UIAbility組件啟動模式肆汹。
-
在源端
UIAbility
中實現(xiàn)onContinue()
回調(diào)。當
UIAbility
實例觸發(fā)遷移時予权,onContinue()
回調(diào)在源端被調(diào)用昂勉,開發(fā)者可以在該接口中通過同步或異步的方式來保存遷移數(shù)據(jù),實現(xiàn)應用兼容性檢測扫腺,決定是否支持此次遷移岗照。- 保存遷移數(shù)據(jù):開發(fā)者可以將要遷移的數(shù)據(jù)通過鍵值對的方式保存在
wantParam
參數(shù)中。 - 應用兼容性檢測:開發(fā)者可以通過從
wantParam
參數(shù)中獲取對端應用的版本號與源端應用版本號做兼容性校驗。開發(fā)者可以在觸發(fā)遷移時從onContinue()
回調(diào)中wantParam.version
獲取到遷移對端應用的版本號與遷移源端應用版本號做兼容校驗攒至。 - 遷移決策:開發(fā)者可以通過
onContinue()
回調(diào)的返回值決定是否支持此次遷移厚者。
- 保存遷移數(shù)據(jù):開發(fā)者可以將要遷移的數(shù)據(jù)通過鍵值對的方式保存在
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import hilog from '@ohos.hilog';
import UIAbility from '@ohos.app.ability.UIAbility';
const TAG: string = '[MigrationAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
export default class MigrationAbility extends UIAbility {
onContinue(wantParam: Record<string, Object>):AbilityConstant.OnContinueResult {
let version = wantParam.version;
let targetDevice = wantParam.targetDevice;
hilog.info(DOMAIN_NUMBER, TAG, `onContinue version = ${version}, targetDevice: ${targetDevice}`); // 準備遷移數(shù)據(jù)
// 獲取源端版本號
let versionSrc: number = -1; // 請?zhí)畛渚唧w獲取版本號的代碼
// 兼容性校驗
if (version !== versionSrc) {
// 在兼容性校驗不通過時返回MISMATCH
return AbilityConstant.OnContinueResult.MISMATCH;
}
// 將要遷移的數(shù)據(jù)保存在wantParam的自定義字段(例如data)中
const continueInput = '遷移的數(shù)據(jù)';
wantParam['data'] = continueInput;
return AbilityConstant.OnContinueResult.AGREE;
}
}
-
源端設(shè)備
UIAbility
實例在冷啟動和熱啟動情況下分別會調(diào)用不同的接口來恢復數(shù)據(jù)和加載UI。
在對端設(shè)備的UIAbility
中迫吐,需要實現(xiàn)onCreate()
/onNewWant()
接口來恢復遷移數(shù)據(jù)库菲。通過在
onCreate()
/onNewWant()
回調(diào)中檢查launchReason
,可以判斷此次啟動是否有遷移觸發(fā)志膀。開發(fā)者可以從want
中獲取之前保存的遷移數(shù)據(jù)熙宇,并在數(shù)據(jù)恢復后調(diào)用restoreWindowStage()
來觸發(fā)頁面恢復,包括頁面棧信息溉浙。
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import hilog from '@ohos.hilog';
import UIAbility from '@ohos.app.ability.UIAbility';
import type Want from '@ohos.app.ability.Want';
const TAG: string = '[MigrationAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
export default class MigrationAbility extends UIAbility {
storage : LocalStorage = new LocalStorage();
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onCreate');
if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
// 將上述保存的數(shù)據(jù)從want.parameters中取出恢復
let continueInput = '';
if (want.parameters !== undefined) {
continueInput = JSON.stringify(want.parameters.data);
hilog.info(DOMAIN_NUMBER, TAG, `continue input ${continueInput}`);
}
// 觸發(fā)頁面恢復
this.context.restoreWindowStage(this.storage);
}
}
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(DOMAIN_NUMBER, TAG, 'onNewWant');
if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
// 將上述保存的數(shù)據(jù)從want.parameters中取出恢復
let continueInput = '';
if (want.parameters !== undefined) {
continueInput = JSON.stringify(want.parameters.data);
hilog.info(DOMAIN_NUMBER, TAG, `continue input ${continueInput}`);
}
// 觸發(fā)頁面恢復
this.context.restoreWindowStage(this.storage);
}
}
}
可選配置遷移能力
動態(tài)配置遷移能力
從API version 10開始烫止,提供了支持動態(tài)配置遷移能力的功能。即應用可以根據(jù)實際使用場景戳稽,在需要遷移時開啟應用遷移能力馆蠕;在業(yè)務不需要遷移時則可以關(guān)閉遷移能力。
開發(fā)者可以通過調(diào)用 setMissionContinueState()
接口對遷移能力進行設(shè)置惊奇。默認狀態(tài)下互躬,應用的遷移能力為ACTIVE狀態(tài),即遷移能力開啟颂郎,可以遷移吼渡。
設(shè)置遷移能力的時機
遷移能力的改變可以根據(jù)實際業(yè)務需求和代碼實現(xiàn),發(fā)生在應用生命周期的絕大多數(shù)時機祖秒。本文介紹常用的幾種配置方式。
在UIAbility
的 onCreate()
回調(diào)中調(diào)用接口舟奠,可以在應用創(chuàng)建時設(shè)置應用的遷移狀態(tài)竭缝。
// MigrationAbility.ets
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import hilog from '@ohos.hilog';
import UIAbility from '@ohos.app.ability.UIAbility';
import type Want from '@ohos.app.ability.Want';
const TAG: string = '[MigrationAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
export default class MigrationAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// ...
this.context.setMissionContinueState(AbilityConstant.ContinueState.INACTIVE, (result) => {
hilog.info(DOMAIN_NUMBER, TAG, `setMissionContinueState: ${JSON.stringify(result)}`);
});
// ...
}
}
在頁面的onPageShow()
回調(diào)中調(diào)用接口,可以設(shè)置單個頁面出現(xiàn)時應用的遷移狀態(tài)沼瘫。
// Page_MigrationAbilityFirst.ets
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
const TAG: string = '[MigrationAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct Page_MigrationAbilityFirst {
private context = getContext(this) as common.UIAbilityContext;
build() {
// ...
}
// ...
onPageShow(){
// 進入該頁面時抬纸,將應用設(shè)置為可遷移狀態(tài)
this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
});
}
}
在某個組件的觸發(fā)事件中設(shè)置應用遷移能力。
// Page_MigrationAbilityFirst.ets
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
import promptAction from '@ohos.promptAction';
const TAG: string = '[MigrationAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct Page_MigrationAbilityFirst {
private context = getContext(this) as common.UIAbilityContext;
build() {
// ...
Button() {
// ...
}
.onClick(()=>{
// 點擊該按鈕時耿戚,將應用設(shè)置為可遷移狀態(tài)
this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
});
})
}
}
保證遷移連續(xù)性
由于遷移加載時湿故,對端拉起的應用可能執(zhí)行過自己的遷移狀態(tài)設(shè)置命令(例如,冷啟動時對端在 onCreate()
中設(shè)置了 INACTIVE 膜蛔;熱啟動時對端已打開了不可遷移的頁面坛猪,遷移狀態(tài)為 INACTIVE 等情況)。為了保證遷移過后的應用依然具有可以遷移回源端的能力皂股,應在 onCreate()
/ onNewWant()
的遷移調(diào)用判斷中墅茉,將遷移狀態(tài)設(shè)置為 ACTIVE 。
// MigrationAbility.ets
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import hilog from '@ohos.hilog';
import UIAbility from '@ohos.app.ability.UIAbility';
import type Want from '@ohos.app.ability.Want';
const TAG: string = '[MigrationAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
export default class MigrationAbility extends UIAbility {
// ...
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// ...
// 調(diào)用原因為遷移時,設(shè)置狀態(tài)為可遷移就斤,應對冷啟動情況
if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
hilog.info(`setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
});
}
}
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// ...
// 調(diào)用原因為遷移時悍募,設(shè)置狀態(tài)為可遷移,應對熱啟動情況
if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
this.context.setMissionContinueState(AbilityConstant.ContinueState.ACTIVE, (result) => {
hilog.info(DOMAIN_NUMBER, TAG, `setMissionContinueState ACTIVE result: ${JSON.stringify(result)}`);
});
}
}
// ...
}
按需遷移頁面棧
支持應用動態(tài)選擇是否進行頁面椦蠡恢復(默認進行頁面棧信息恢復)坠宴。如果應用不想使用系統(tǒng)默認恢復的頁面棧,則可以設(shè)置不進行頁面棧遷移绷旗,而需要在onWindowStageRestore()
設(shè)置遷移后進入的頁面喜鼓,參數(shù)定義見 SUPPORT_CONTINUE_PAGE_STACK_KEY 。
應用在源端的頁面棧中存在Index和Second路由刁标,而在對端恢復時不需要按照源端頁面棧進行恢復颠通,需要恢復到指定頁面。
例如膀懈,UIAbility
遷移不需要自動遷移頁面棧信息顿锰。
// MigrationAbility.ets
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import hilog from '@ohos.hilog';
import UIAbility from '@ohos.app.ability.UIAbility';
import wantConstant from '@ohos.app.ability.wantConstant';
import type window from '@ohos.window';
const TAG: string = '[MigrationAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
export default class MigrationAbility extends UIAbility {
// ...
onContinue(wantParam: Record<string, Object>):AbilityConstant.OnContinueResult {
hilog.info(DOMAIN_NUMBER, TAG, `onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`);
wantParam[wantConstant.Params.SUPPORT_CONTINUE_PAGE_STACK_KEY] = false;
return AbilityConstant.OnContinueResult.AGREE;
}
onWindowStageRestore(windowStage: window.WindowStage) : void {
// 若不需要自動遷移頁面棧信息,則需要在此處設(shè)置應用遷移后進入的頁面
windowStage.loadContent('pages/page_migrationability/Page_MigrationAbilityThird', (err, data) => {
if (err.code) {
hilog.error(DOMAIN_NUMBER, TAG, 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
});
}
}
按需退出
支持應用動態(tài)選擇遷移成功后是否退出遷移源端應用(默認遷移成功后退出遷移源端應用)启搂。如果應用不想讓系統(tǒng)自動退出遷移源端應用硼控,則可以設(shè)置不退出,參數(shù)定義見 SUPPORT_CONTINUE_SOURCE_EXIT_KEY 胳赌。
示例:UIAbility
設(shè)置遷移成功后牢撼,源端不需要退出遷移應用。
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import hilog from '@ohos.hilog';
import UIAbility from '@ohos.app.ability.UIAbility';
import wantConstant from '@ohos.app.ability.wantConstant';
const TAG: string = '[MigrationAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
export default class MigrationAbility extends UIAbility {
// ...
onContinue(wantParam: Record<string, Object>):AbilityConstant.OnContinueResult {
hilog.info(DOMAIN_NUMBER, TAG, `onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`);
wantParam[wantConstant.Params.SUPPORT_CONTINUE_SOURCE_EXIT_KEY] = false;
return AbilityConstant.OnContinueResult.AGREE;
}
}
跨端遷移中的數(shù)據(jù)遷移
當前支持三種不同的數(shù)據(jù)遷移方式疑苫,開發(fā)者可以根據(jù)實際使用需要進行選擇熏版。
說明:
部分ArkUI組件支持通過配置restoreId
的方式,在遷移后將特定狀態(tài)恢復到對端設(shè)備捍掺。
如果涉及分布式對象和分布式文件遷移時應注意:
- 需要申請
ohos.permission.DISTRIBUTED_DATASYNC
權(quán)限撼短,配置方式請參見 聲明權(quán)限 。 - 同時需要在應用首次啟動時彈窗向用戶申請授權(quán)挺勿,使用方式請參見 向用戶申請授權(quán) 曲横。
使用wantParam遷移數(shù)據(jù)
在需要遷移的數(shù)據(jù)較少(100KB以下)時,開發(fā)者可以選擇在wantParam
中增加字段進行數(shù)據(jù)遷移不瓶。示例如下:
import UIAbility from '@ohos.app.ability.UIAbility';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import Want from '@ohos.app.ability.Want';
const TAG: string = '[MigrationAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
export default class MigrationAbility extends UIAbility {
// 源端保存
onContinue(wantParam: Record<string, Object>):AbilityConstant.OnContinueResult {
// 將要遷移的數(shù)據(jù)保存在wantParam的自定義字段(例如data)中
const continueInput = '遷移的數(shù)據(jù)';
wantParam['data'] = continueInput;
return AbilityConstant.OnContinueResult.AGREE;
}
// 對端恢復
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
if (launchParam.launchReason === AbilityConstant.LaunchReason.CONTINUATION) {
// 將上述保存的數(shù)據(jù)取出恢復
let continueInput = '';
if (want.parameters !== undefined) {
continueInput = JSON.stringify(want.parameters.data);
hilog.info(DOMAIN_NUMBER, TAG, `continue input ${continueInput}`);
}
// 觸發(fā)頁面恢復
this.context.restoreWindowStage(this.storage);
}
}
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
let continueInput = '';
if (want.parameters !== undefined) {
continueInput = JSON.stringify(want.parameters.data);
hilog.info(DOMAIN_NUMBER, TAG, `continue input ${JSON.stringify(continueInput)}`);
}
// 觸發(fā)頁面恢復
this.context.restoreWindowStage(this.storage);
}
}
}
使用分布式對象遷移數(shù)據(jù)
當需要遷移的數(shù)據(jù)較大(100KB以上)時禾嫉,可以選擇 分布式對象 進行數(shù)據(jù)遷移。
- 在源端
onContinue()
接口中創(chuàng)建一個分布式數(shù)據(jù)對象DataObject
蚊丐,將所要遷移的數(shù)據(jù)填充到分布式對象數(shù)據(jù)中熙参,并將生成的sessionId
通過want
傳遞到對端。 - 對端在
onCreate()/onNewWant
中進行數(shù)據(jù)恢復時麦备,可以從want中讀取該sessionId
尊惰,通過分布式對象恢復數(shù)據(jù)讲竿。
使用分布式文件遷移數(shù)據(jù)
當需要遷移的數(shù)據(jù)較大(100KB以上)時,也可以選擇分布式文件進行數(shù)據(jù)遷移弄屡。相比于分布式對象题禀,分布式文件更適用于需要傳輸?shù)臄?shù)據(jù)為文件的場景。在源端將數(shù)據(jù)寫入分布式文件路徑后膀捷,對端遷移后拉起的應用能夠在同個分布式文件路徑下訪問到該文件迈嘹。
驗證指導
為方便開發(fā)者驗證已開發(fā)的可遷移應用,系統(tǒng)提供了一個全局任務中心demo作為遷移的入口全庸。下面介紹通過安裝全局任務中心來驗證遷移的方式秀仲。
1. 編譯安裝全局任務中心
配置環(huán)境
public-SDK不支持開發(fā)者使用所有的系統(tǒng)API,例如:全局任務中心使用的 @ohos.distributedDeviceManager 不包括在public_SDK中壶笼。因此為了正確編譯安裝全局任務中心神僵,開發(fā)者需要替換full-SDK,具體操作可參見替換指南覆劈。
說明:
本文中的截圖僅為參考保礼,具體的顯示界面請以實際使用的DevEco Studio和SDK的版本為準。
下載MissionCenter_Demo 示例代碼
編譯工程文件
a.新建一個工程责语,找到對應的文件夾替換下載文件
b.自動簽名炮障,編譯安裝。
DevEco的自動簽名模板默認簽名權(quán)限為normal級坤候。而本應用設(shè)計到ohos.permission.MANAGE_MISSIONS權(quán)限為system_core級別胁赢。自動生成的簽名無法獲得足夠的權(quán)限,所以需要將權(quán)限升級為system_core級別白筹,然后簽名智末。
c.系統(tǒng)權(quán)限設(shè)置(以api10目錄為例): 將Sdk目錄下的openharmony\api版本(如:10)\toolchains\lib\UnsignedReleasedProfileTemplate.json文件中的"apl":"normal"改為"apl":"system_core"。
- 點擊file->Project Structure徒河。
- 點擊Signing Configs 點擊OK系馆。
- 連接開發(fā)板運行生成demo。
2. 設(shè)備組網(wǎng)
- 打開A虚青,B兩設(shè)備的計算器它呀。
- 點擊右上角箭頭選擇B設(shè)備螺男。
- 在B設(shè)備選擇信任設(shè)備棒厘,彈出PIN碼。
- 在A設(shè)備輸入PIN碼下隧。
- 已組網(wǎng)成功奢人,驗證方法:在A設(shè)備輸入數(shù)字,B設(shè)備同步出現(xiàn)則證明組網(wǎng)成功淆院。
3. 發(fā)起遷移
- 在B設(shè)備打開多設(shè)備協(xié)同權(quán)限的應用何乎,A設(shè)備打開全局任務中心demo,A設(shè)備出現(xiàn)A設(shè)備名稱(即:本機:OpenHarmony 3.2)和B設(shè)備名稱(OpenHarmony 3.2)。
- 點擊B設(shè)備名稱支救,然后出現(xiàn)B設(shè)備的應用抢野。
- 最后將應用拖拽到A設(shè)備名稱處,A設(shè)備應用被拉起各墨,B設(shè)備應用退出指孤。
寫在最后
- 如果你覺得這篇內(nèi)容對你還蠻有幫助,我想邀請你幫我三個小忙:
- 點贊贬堵,轉(zhuǎn)發(fā)恃轩,有你們的 『點贊和評論』,才是我創(chuàng)造的動力黎做。
- 關(guān)注小編叉跛,同時可以期待后續(xù)文章ing??,不定期分享原創(chuàng)知識蒸殿。
- 想要獲取更多完整鴻蒙最新學習知識點筷厘,請移步前往小編:
https://gitee.com/MNxiaona/733GH/blob/master/jianshu