場景介紹
傳統(tǒng)方式下乎婿,設備之間的數(shù)據(jù)同步测僵,需要開發(fā)者完成消息處理邏輯,包括:建立通信鏈接谢翎、消息收發(fā)處理捍靠、錯誤重試沐旨、數(shù)據(jù)沖突解決等操作,工作量非常大剂公。而且設備越多希俩,調(diào)試復雜度也將同步增加。
其實設備之間的狀態(tài)纲辽、消息發(fā)送進度、發(fā)送的數(shù)據(jù)等都是“變量”璃搜。如果這些變量支持“全局”訪問拖吼,那么開發(fā)者跨設備訪問這些變量就能像操作本地變量一樣,從而能夠自動高效这吻、便捷地實現(xiàn)數(shù)據(jù)多端同步吊档。
分布式數(shù)據(jù)對象即實現(xiàn)了對“變量”的“全局”訪問。向應用開發(fā)者提供內(nèi)存對象的創(chuàng)建唾糯、查詢怠硼、刪除、修改移怯、訂閱等基本數(shù)據(jù)對象的管理能力香璃,同時具備分布式能力。為開發(fā)者在分布式應用場景下提供簡單易用的JS接口舟误,輕松實現(xiàn)多設備間同應用的數(shù)據(jù)協(xié)同葡秒,同時設備間可以監(jiān)聽對象的狀態(tài)和數(shù)據(jù)變更。滿足超級終端場景下嵌溢,相同應用多設備間的數(shù)據(jù)對象協(xié)同需求眯牧。與傳統(tǒng)方式相比,分布式數(shù)據(jù)對象大大減少了開發(fā)者的工作量赖草。
基本概念
分布式內(nèi)存數(shù)據(jù)庫
分布式內(nèi)存數(shù)據(jù)庫將數(shù)據(jù)緩存在內(nèi)存中学少,以便應用獲得更快的數(shù)據(jù)存取速度,不會將數(shù)據(jù)進行持久化秧骑。若數(shù)據(jù)庫關(guān)閉版确,則數(shù)據(jù)不會保留。-
分布式數(shù)據(jù)對象
分布式數(shù)據(jù)對象是一個JS對象型的封裝腿堤。每一個分布式數(shù)據(jù)對象實例會創(chuàng)建一個內(nèi)存數(shù)據(jù)庫中的數(shù)據(jù)表阀坏,每個應用程序創(chuàng)建的內(nèi)存數(shù)據(jù)庫相互隔離,對分布式數(shù)據(jù)對象的“讀取”或“賦值”會自動映射到對應數(shù)據(jù)庫的get/put操作笆檀。分布式數(shù)據(jù)對象的生命周期包括以下狀態(tài):
- 未初始化:未實例化忌堂,或已被銷毀。
- 本地數(shù)據(jù)對象:已創(chuàng)建對應的數(shù)據(jù)表酗洒,但是還無法進行數(shù)據(jù)同步士修。
- 分布式數(shù)據(jù)對象:已創(chuàng)建對應的數(shù)據(jù)表枷遂,設備在線且組網(wǎng)內(nèi)設置同樣sessionId的對象數(shù)>=2甜刻,可以跨設備同步數(shù)據(jù)雹嗦。若設備掉線或?qū)essionId置為空芥喇,分布式數(shù)據(jù)對象退化為本地數(shù)據(jù)對象吨拍。
運作機制
圖1 分布式數(shù)據(jù)對象運作機制
分布式數(shù)據(jù)對象生長在分布式內(nèi)存數(shù)據(jù)庫之上程奠,在分布式內(nèi)存數(shù)據(jù)庫上進行了JS對象型的封裝证九,能像操作本地變量一樣操作分布式數(shù)據(jù)對象残制,數(shù)據(jù)的跨設備同步由系統(tǒng)自動完成遭贸。
JS對象型存儲與封裝機制
為每個分布式數(shù)據(jù)對象實例創(chuàng)建一個內(nèi)存數(shù)據(jù)庫雹锣,通過SessionId標識网沾,每個應用程序創(chuàng)建的內(nèi)存數(shù)據(jù)庫相互隔離。
在分布式數(shù)據(jù)對象實例化的時候蕊爵,(遞歸)遍歷對象所有屬性辉哥,使用“Object.defineProperty”定義所有屬性的set和get方法,set和get中分別對應數(shù)據(jù)庫一條記錄的put和get操作攒射,Key對應屬性名醋旦,Value對應屬性值。
在開發(fā)者對分布式數(shù)據(jù)對象進行“讀取”或者“賦值”的時候会放,都會自動調(diào)用到set和get方法饲齐,映射到對應數(shù)據(jù)庫的操作。
表1 分布式數(shù)據(jù)對象和分布式數(shù)據(jù)庫的對應關(guān)系
分布式對象實例 | 對象實例 | 屬性名稱 | 屬性值 |
---|---|---|---|
分布式內(nèi)存數(shù)據(jù)庫 | 一個數(shù)據(jù)庫(sessionID標識) | 一條數(shù)據(jù)庫記錄的key | 一條數(shù)據(jù)庫記錄的value |
跨設備同步和數(shù)據(jù)變更通知機制
分布式數(shù)據(jù)對象鸦概,最重要的功能就是對象之間的數(shù)據(jù)同步箩张。可信組網(wǎng)內(nèi)的設備可以在本地創(chuàng)建分布式數(shù)據(jù)對象窗市,并設置sessionID先慷。不同設備上的分布式數(shù)據(jù)對象,通過設置相同的sessionID咨察,建立對象之間的同步關(guān)系论熙。
如下圖所示,設備A和設備B上的“分布式數(shù)據(jù)對象1”摄狱,其sessionID均為session1脓诡,這兩個對象建立了session1的同步關(guān)系。
圖2 對象的同步關(guān)系
一個同步關(guān)系中媒役,一個設備只能有一個對象加入祝谚。比如上圖中,設備A的“分布式數(shù)據(jù)對象1”已經(jīng)加入了session1的同步關(guān)系酣衷,所以設備A的“分布式數(shù)據(jù)對象2”就加入失敗了交惯。
建立同步關(guān)系后,每個Session有一份共享對象數(shù)據(jù)。加入了同一個Session的對象席爽,支持以下操作:
(1)讀取/修改Session中的數(shù)據(jù)意荤。
(2)監(jiān)聽數(shù)據(jù)變更,感知其他設備對共享對象數(shù)據(jù)的修改只锻。
(3)監(jiān)聽狀態(tài)變更玖像,感知其他設備的加入和退出。
同步的最小單位
關(guān)于分布式數(shù)據(jù)對象的數(shù)據(jù)同步齐饮,值得注意的是捐寥,同步的最小單位是“屬性”。比如祖驱,下圖中對象1包含三個屬性:name上真、age和parents。當其中一個屬性變更時羹膳,則數(shù)據(jù)同步時只需同步此變更的屬性。
圖3 數(shù)據(jù)同步視圖
對象持久化緩存機制
分布式對象主要運行在應用程序的進程空間根竿。當調(diào)用分布式對象持久化接口時陵像,通過分布式數(shù)據(jù)庫對對象進行持久化和同步,進程退出后數(shù)據(jù)也不會丟失寇壳。
該場景是分布式對象的擴展場景醒颖,主要用于以下情況:
在設備上創(chuàng)建持久化對象后APP退出,重新打開APP壳炎,創(chuàng)建持久化對象泞歉,加入同一個Session,數(shù)據(jù)可以恢復到APP退出前的數(shù)據(jù)匿辩。
在設備A上創(chuàng)建持久化對象并同步后持久化到設備B后腰耙,A設備的APP退出,設備B打開APP铲球,創(chuàng)建持久化對象挺庞,加入同一個Session,數(shù)據(jù)可以恢復到A設備退出前的數(shù)據(jù)稼病。
資產(chǎn)同步機制
在分布式對象中选侨,可以使用資產(chǎn)類型來描述本地實體資產(chǎn)文件,分布式對象跨設備同步時然走,該文件會和數(shù)據(jù)一起同步到其他設備上援制。當前只支持資產(chǎn)類型,不支持資產(chǎn)類型數(shù)組芍瑞。如需同步多個資產(chǎn)晨仑,可將每個資產(chǎn)作為分布式對象的一個根屬性實現(xiàn)。
融合資產(chǎn)沖突解決機制
當分布式對象中包含的資產(chǎn)和關(guān)系型數(shù)據(jù)庫中包含的資產(chǎn)指向同一個實體資產(chǎn)文件,即兩個資產(chǎn)的Uri相同時寻歧,就會存在沖突掌栅,我們把這種資產(chǎn)稱為融合資產(chǎn)。若想解決融合資產(chǎn)的沖突码泛,需要先進行資產(chǎn)的綁定猾封。當應用退出session后,綁定關(guān)系隨之消失噪珊。
約束限制
不同設備間只有相同bundleName的應用才能直接同步晌缘。
分布式數(shù)據(jù)對象的數(shù)據(jù)同步發(fā)生在同一個應用程序下,且同sessionID之間痢站。
不建議創(chuàng)建過多的分布式數(shù)據(jù)對象磷箕,每個分布式數(shù)據(jù)對象將占用100-150KB內(nèi)存。
每個分布式數(shù)據(jù)對象大小不超過500KB阵难。
設備A修改1KB數(shù)據(jù)岳枷,設備B收到變更通知,50ms內(nèi)完成呜叫。
單個應用程序最多只能創(chuàng)建16個分布式數(shù)據(jù)對象實例空繁。
考慮到性能和用戶體驗,最多不超過3個設備進行數(shù)據(jù)協(xié)同朱庆。
如對復雜類型的數(shù)據(jù)進行修改盛泡,僅支持修改根屬性,暫不支持下級屬性修改娱颊。資產(chǎn)同步機制中傲诵,資產(chǎn)類型的數(shù)據(jù)支持下一級屬性修改。
支持JS接口間的互通箱硕,與其他語言不互通拴竹。
接口說明
以下是分布式對象跨設備數(shù)據(jù)同步功能的相關(guān)接口,大部分為異步接口颅痊。異步接口均有callback和Promise兩種返回形式殖熟,下表均以callback形式為例,更多接口及使用方式請見分布式數(shù)據(jù)對象斑响。
接口名稱 | 描述 |
---|---|
create(context: Context, source: object): DataObject | 創(chuàng)建并得到一個分布式數(shù)據(jù)對象實例菱属。 |
genSessionId(): string | 創(chuàng)建一個sessionId,可作為分布式數(shù)據(jù)對象的sessionId舰罚。 |
setSessionId(sessionId: string, callback: AsyncCallback<void>): void | 設置同步的sessionId纽门,當可信組網(wǎng)中有多個設備時,多個設備間的對象如果設置為同一個sessionId营罢,就能自動同步赏陵。 |
setSessionId(callback: AsyncCallback<void>): void | 退出所有已加入的session饼齿。 |
on(type: 'change', callback: (sessionId: string, fields: Array<string>) => void): void | 監(jiān)聽分布式數(shù)據(jù)對象的數(shù)據(jù)變更。 |
off(type: 'change', callback?: (sessionId: string, fields: Array<string>) => void): void | 取消監(jiān)聽分布式數(shù)據(jù)對象的數(shù)據(jù)變更蝙搔。 |
on(type: 'status', callback: (sessionId: string, networkId: string, status: 'online' | 'offline' ) => void): void | 監(jiān)聽分布式數(shù)據(jù)對象的上下線缕溉。 |
off(type: 'status', callback?: (sessionId: string, networkId: string, status: 'online' |'offline' ) => void): void | 取消監(jiān)聽分布式數(shù)據(jù)對象的上下線。 |
save(deviceId: string, callback: AsyncCallback<SaveSuccessResponse>): void | 保存分布式數(shù)據(jù)對象吃型。 |
revokeSave(callback: AsyncCallback<RevokeSaveSuccessResponse>): void | 撤回保存的分布式數(shù)據(jù)對象证鸥。 |
bindAssetStore(assetKey: string, bindInfo: BindInfo, callback: AsyncCallback<void>): void | 綁定融合資產(chǎn)。 |
開發(fā)步驟
跨設備數(shù)據(jù)同步
以一次分布式數(shù)據(jù)對象同步為例勤晚,說明開發(fā)步驟枉层。
-
導入
@ohos.data.distributedDataObject
模塊。import distributedDataObject from '@ohos.data.distributedDataObject';
-
請求權(quán)限赐写。
- 需要申請ohos.permission.DISTRIBUTED_DATASYNC權(quán)限鸟蜡,配置方式請參見聲明權(quán)限。
- 同時需要在應用首次啟動時彈窗向用戶申請授權(quán)挺邀,使用方式請參見向用戶申請授權(quán)揉忘。
-
創(chuàng)建并得到一個分布式數(shù)據(jù)對象實例。
Stage模型示例:
// 導入模塊 import distributedDataObject from '@ohos.data.distributedDataObject'; import UIAbility from '@ohos.app.ability.UIAbility'; import { BusinessError } from '@ohos.base'; import window from '@ohos.window'; class ParentObject { mother: string father: string constructor(mother: string, father: string) { this.mother = mother this.father = father } } class SourceObject { name: string | undefined age: number | undefined isVis: boolean | undefined parent: Object | undefined constructor(name: string | undefined, age: number | undefined, isVis: boolean | undefined, parent: ParentObject | undefined) { this.name = name this.age = age this.isVis = isVis this.parent = parent } } class EntryAbility extends UIAbility { onWindowStageCreate(windowStage: window.WindowStage) { let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad'); let source: SourceObject = new SourceObject("jack", 18, false, parentSource); let localObject: distributedDataObject.DataObject = distributedDataObject.create(this.context, source); } }
FA模型示例:
// 導入模塊 import distributedDataObject from '@ohos.data.distributedDataObject'; import featureAbility from '@ohos.ability.featureAbility'; // 獲取context let context = featureAbility.getContext(); class ParentObject { mother: string father: string constructor(mother: string, father: string) { this.mother = mother this.father = father } } class SourceObject { name: string | undefined age: number | undefined isVis: boolean | undefined parent: ParentObject | undefined constructor(name: string | undefined, age: number | undefined, isVis: boolean | undefined, parent: ParentObject | undefined) { this.name = name this.age = age this.isVis = isVis this.parent = parent } } let parentSource: ParentObject = new ParentObject('jack mom', 'jack Dad'); let source: SourceObject = new SourceObject("jack", 18, false, parentSource); // 創(chuàng)建對象端铛,該對象包含4個屬性類型:string癌淮、number、boolean和Object let localObject: distributedDataObject.DataObject = distributedDataObject.create(context, source);
-
加入同步組網(wǎng)沦补。同步組網(wǎng)中的數(shù)據(jù)對象分為發(fā)起方和被拉起方。
// 設備1加入sessionId let sessionId: string = '123456'; localObject.setSessionId(sessionId); // 和設備1協(xié)同的設備2加入同一個session // 創(chuàng)建對象咪橙,該對象包含4個屬性類型:string夕膀、number、boolean和Object let remoteSource: SourceObject = new SourceObject(undefined, undefined, undefined, undefined); let remoteObject: distributedDataObject.DataObject = distributedDataObject.create(this.context, remoteSource); // 收到status上線后remoteObject同步數(shù)據(jù)美侦,即name變成jack产舞,age變成18 remoteObject.setSessionId(sessionId);
-
監(jiān)聽對象數(shù)據(jù)變更〔な#可監(jiān)聽對端數(shù)據(jù)的變更易猫,以callback作為變更回調(diào)實例。
localObject.on("change", (sessionId: string, fields: Array<string>) => { console.info("change" + sessionId); if (fields != null && fields != undefined) { for (let index: number = 0; index < fields.length; index++) { console.info(`The element ${localObject[fields[index]]} changed.`); } } });
-
修改對象屬性具壮,對象屬性支持基本類型(數(shù)字類型准颓、布爾類型、字符串類型)以及復雜類型(數(shù)組棺妓、基本類型嵌套等)攘已。
localObject["name"] = 'jack1'; localObject["age"] = 19; localObject["isVis"] = false; let parentSource1: ParentObject = new ParentObject('jack1 mom', 'jack1 Dad'); localObject["parent"] = parentSource1;
說明:
針對復雜類型的數(shù)據(jù)修改,目前僅支持對根屬性的修改怜跑,暫不支持對下級屬性的修改样勃。
// 支持的修改方式
let parentSource1: ParentObject = new ParentObject('mom', 'Dad');
localObject["parent"] = parentSource1;
// 不支持的修改方式
localObject["parent"]["mother"] = 'mom';
-
訪問對象。可以通過直接獲取的方式訪問到分布式數(shù)據(jù)對象的屬性峡眶,且該數(shù)據(jù)為組網(wǎng)內(nèi)的最新數(shù)據(jù)剧防。
console.info(`name:${localObject['name']}`);
-
刪除監(jiān)聽數(shù)據(jù)變更”栌#可以指定刪除監(jiān)聽的數(shù)據(jù)變更回調(diào)峭拘;也可以不指定,這將會刪除該分布式數(shù)據(jù)對象的所有數(shù)據(jù)變更回調(diào)搏熄。
// 刪除變更回調(diào) localObject.off('change', (sessionId: string, fields: Array<string>) => { console.info("change" + sessionId); if (fields != null && fields != undefined) { for (let index: number = 0; index < fields.length; index++) { console.info("changed !" + fields[index] + " " + localObject[fields[index]]); } } }); // 刪除所有的變更回調(diào) localObject.off('change');
-
監(jiān)聽分布式數(shù)據(jù)對象的上下線棚唆。可以監(jiān)聽對端分布式數(shù)據(jù)對象的上下線心例。
localObject.on('status', (sessionId: string, networkId: string, status: 'online' | 'offline') => { console.info("status changed " + sessionId + " " + status + " " + networkId); // 業(yè)務處理 });
-
保存和撤回已保存的數(shù)據(jù)對象宵凌。
// 保存數(shù)據(jù)對象,如果應用退出后組網(wǎng)內(nèi)設備需要恢復對象數(shù)據(jù)時調(diào)用 localObject.save("local").then((result: distributedDataObject.SaveSuccessResponse) => { console.info(`Succeeded in saving. SessionId:${result.sessionId},version:${result.version},deviceId:${result.deviceId}`); }).catch((err: BusinessError) => { console.error(`Failed to save. Code:${err.code},message:${err.message}`); }); // 撤回保存的數(shù)據(jù)對象 localObject.revokeSave().then((result: distributedDataObject.RevokeSaveSuccessResponse) => { console.info(`Succeeded in revokeSaving. Session:${result.sessionId}`); }).catch((err: BusinessError) => { console.error(`Failed to revokeSave. Code:${err.code},message:${err.message}`); });
-
刪除監(jiān)聽分布式數(shù)據(jù)對象的上下線止后∠贡梗可以指定刪除監(jiān)聽的上下線回調(diào);也可以不指定译株,這將會刪除該分布式數(shù)據(jù)對象的所有上下線回調(diào)瓜喇。
// 刪除上下線回調(diào) localObject.off('status', (sessionId: string, networkId: string, status: 'online' | 'offline') => { console.info("status changed " + sessionId + " " + status + " " + networkId); // 業(yè)務處理 }); // 刪除所有的上下線回調(diào) localObject.off('status');
-
退出同步組網(wǎng)。分布式數(shù)據(jù)對象退出組網(wǎng)后歉糜,本地的數(shù)據(jù)變更對端不會同步乘寒。
localObject.setSessionId(() => { console.info('leave all session.'); });
跨設備資產(chǎn)同步
分布式對象中加入資產(chǎn)類型屬性,可以觸發(fā)資產(chǎn)同步機制匪补,將資產(chǎn)類型屬性所描述的文件同步到其他設備伞辛。持有資產(chǎn)文件的設備為發(fā)起端,得到資產(chǎn)文件的設備為接收端夯缺。
-
導入
@ohos.data.distributedDataObject
和@ohos.data.commonType
模塊蚤氏。import distributedDataObject from '@ohos.data.distributedDataObject'; import commonType from '@ohos.data.commonType';
-
請求權(quán)限。
- 需要申請ohos.permission.DISTRIBUTED_DATASYNC權(quán)限踊兜。
- 同時需要在應用首次啟動時彈窗向用戶申請授權(quán)竿滨。
-
發(fā)起端創(chuàng)建包含資產(chǎn)的分布式對象并加入組網(wǎng)。
import UIAbility from '@ohos.app.ability.UIAbility'; import type window from '@ohos.window'; import distributedDataObject from '@ohos.data.distributedDataObject'; import commonType from '@ohos.data.commonType'; import type { BusinessError } from '@ohos.base'; class Note { title: string | undefined text: string | undefined attachment: commonType.Asset | undefined constructor(title: string | undefined, text: string | undefined, attachment: commonType.Asset | undefined) { this.title = title; this.text = text; this.attachment = attachment; } } class EntryAbility extends UIAbility { onWindowStageCreate(windowStage: window.WindowStage) { let attachment: commonType.Asset = { name: 'test_img.jpg', uri: 'file://com.example.myapplication/data/storage/el2/distributedfiles/dir/test_img.jpg', path: '/dir/test_img.jpg', createTime: '2024-01-02 10:00:00', modifyTime: '2024-01-02 10:00:00', size: '5', status: commonType.AssetStatus.ASSET_NORMAL } // 創(chuàng)建一個自定義筆記類型捏境,其中包含一張圖片資產(chǎn) let note: Note = new Note('test', "test", attachment); let localObject: distributedDataObject.DataObject = distributedDataObject.create(this.context, note); localObject.setSessionId('123456'); } }
-
接收端創(chuàng)建分布式對象并加入組網(wǎng)
let note: Note = new Note(undefined, undefined, undefined); let receiverObject: distributedDataObject.DataObject = distributedDataObject.create(this.context, note); receiverObject.on('change', (sessionId: string, fields: Array<string>) => { if (fields.includes('attachment')) { // 接收端監(jiān)聽到資產(chǎn)類型屬性的數(shù)據(jù)變更時于游,代表其所描述的資產(chǎn)文件同步完成 console.info('attachment synchronization completed'); } }); receiverObject.setSessionId('123456');
-
若資產(chǎn)為融合資產(chǎn),可以創(chuàng)建綁定信息垫言,綁定融合資產(chǎn)曙砂,以解決融合資產(chǎn)的沖突。
const bindInfo: distributedDataObject.BindInfo = { storeName: 'notepad', tableName: 'note_t', primaryKey: { 'uuid': '00000000-0000-0000-0000-000000000000' }, field: 'attachment', assetName: attachment.name } localObject.bindAssetStore('attachment', bindInfo, (err: BusinessError) => { if (err) { console.error('bindAssetStore failed.'); } console.info('bindAssetStore success.'); });
寫在最后
- 如果你覺得這篇內(nèi)容對你還蠻有幫助骏掀,我想邀請你幫我三個小忙:
- 點贊鸠澈,轉(zhuǎn)發(fā)柱告,有你們的 『點贊和評論』,才是我創(chuàng)造的動力笑陈。
- 關(guān)注小編际度,同時可以期待后續(xù)文章ing??,不定期分享原創(chuàng)知識涵妥。
- 想要獲取更多完整鴻蒙最新學習知識點乖菱,請移步前往小編:
https://gitee.com/MNxiaona/733GH/blob/master/jianshu