鴻蒙原生應用開發——分散式資料物件

HarmonyOS開發者社群發表於2023-12-08

01、什麼是分散式資料物件

在可信組網環境下,多個相互組網認證的裝置將各自建立的物件加入同一個 sessionId,使得加入的多個資料物件之間可以同步資料,也就是說,當某一資料物件屬性發生變更時,其他資料物件會檢測到這一變更,同時將自身屬性更新。此時,該 sessionId 下的所有資料物件屬性相同,這樣的資料物件稱之為分散式資料物件。此外,分散式資料物件可以被動退出 sessionId,當分散式資料物件退出 sessionId 後,該物件將檢測不到其他物件的變更。

02、分散式資料物件能力

1、  分散式資料物件建立

2、  分散式資料物件查詢

3、  分散式資料物件修改

4、  分散式資料物件刪除

5、  分散式資料物件儲存

6、  分散式資料物件訂閱(資料變更,上下線)

7、分散式資料物件加入、退出分散式組網

03、前提準備


1、  開發工具:DevEco Studio 3.1.0.501

2、API:9

3、  SDK 版本:3.2.12.5


04、建立一個新的專案

新建專案,選擇 API9 版本,stage 模型。


鴻蒙原生應用開發——分散式資料物件

05、許可權申請


1、  使用到的許可權

○  ohos.permission.DISTRIBUTED_DATASYNC

○  允許不同裝置間的資料交換

○  許可權級別:normal

○  授權方式:user_grant

○  ACL 使能:TRUE

2、配置檔案申明

首先,在專案的模組級目錄下找到並開啟 module.json5 檔案,如下圖:


鴻蒙原生應用開發——分散式資料物件

在 module 下的物件裡新增如下申明:


鴻蒙原生應用開發——分散式資料物件

此時,配置檔案中的許可權申明就完成了,但是,此時我們還不能獲得這些許可權。由於 ohos.permission.DISTRIBUTED_DATASYNC 許可權是 ACL 使能為 TRUE 的許可權,需要在簽名工具檔案中說明一下。

如何找到對應的簽名工具檔案呢?我們在安裝 DevEco Studio 的時候是下載好了 OpenHarmony 的 SDK 的,此時在 OpenHarmony 資料夾中,開啟 “Sdk\OpenHarmony SDK 版本\toolchains\lib” 該路徑,此時在 lib 資料夾中,我們們可以找到兩個 json 檔案,分別為 UnsgnedDebugProfileTemplate.json 和 UnsgnedReleasedProfileTemplate.json,點選並開啟這兩個檔案,新增如下許可權:


鴻蒙原生應用開發——分散式資料物件

3、許可權申請編碼

在申請 ohos.permission.DISTRIBUTED_DATASYNC 許可權時,其文件中將其標註為使用者手動授權的許可權,此時需要我們動態申請許可權,在專案中,我們新建一個 ets 檔案,我這裡取名為 RequestPermission.ets。

首先,匯入以下包:



import abilityAccessCtrl, { 
Permissions } 
from 
'@ohos.abilityAccessCtrl';

import bundleManager 
from 
'@ohos.bundle.bundleManager';

import common 
from 
'@ohos.app.ability.common';




獲取訪問控制模組物件例項:



let atManager = abilityAccessCtrl.
createAtManager();




編寫如下方法(這裡使用的是非同步函式):



export 
async 
function 
checkAccessTokenID(
permission: 
Array<Permissions>) {
  
// 獲取應用程式的accessTokenID
  
let 
tokenId: number;
  
let 
grantStatusArray<abilityAccessCtrl.
GrantStatus> = []
  
try {
    
let 
bundleInfo: bundleManager.
BundleInfo = 
await bundleManager.
getBundleInfoForSelf(bundleManager.
BundleFlag.
GET_BUNDLE_INFO_WITH_APPLICATION);
    
let 
appInfo: bundleManager.
ApplicationInfo = bundleInfo.
appInfo;
    tokenId = appInfo.
accessTokenId;
  } 
catch (err) {
    
console.
error(
`getBundleInfoForSelf failed, code is 
${err.code}, message is 
${err.message}`);
  }
  
// 校驗應用是否被授予許可權,若申請多個許可權,建議迴圈檢查多個許可權
  
for (
let index = 
0;index < permission.
length; index++) {
    
try {
      grantStatus.
push(
await atManager.
checkAccessToken(tokenId, permission[index]))
    } 
catch (err) {
      
console.
error(
`checkAccessToken failed, code is 
${err.code}, message is 
${err.message}`);
    }
  }
  
return grantStatus;
}



export  async  function  checkPermission( context: common.UIAbilityContext, permissions:  Array<Permissions>) {    let  grantStatusArray<abilityAccessCtrl. GrantStatus> =  await  checkAccessTokenID(permissions)    for ( let i =  0; i < grantStatus. length; i++) {      if (grantStatus[i] === abilityAccessCtrl. GrantStatus. PERMISSION_GRANTED) {        console. info( ` ${permissions[i].toString()} 已授權`)     }  else {        //申請許可權        console. info( '開始向使用者申請許可權')        requestPermissionFromUser(context, permissions)     }   } } export  async  function  requestPermissionFromUser( context: common.UIAbilityContext, permissions:  Array<Permissions>) {    // requestPermissionsFromUser會判斷許可權的授權狀態來決定是否喚起彈窗   atManager. requestPermissionsFromUser(context, permissions). then( ( data) => {      let  grantStatusArray<number> = data. authResults      let  length: number = grantStatus. length      for ( let i =  0;i < length; i++) {        if (grantStatus[i] ===  0) {          // 使用者授權,可以繼續訪問目標操作          console. info( ` ${permissions[i].toString()} 許可權申請成功`)       }  else {          // 使用者拒絕授權,提示使用者必須授權才能訪問當前頁面的功能,並引導使用者到系統設定中開啟相應的許可權          console. info( ` ${permissions[i].toString()} 許可權申請被使用者拒絕`)       }     }      // 授權成功   }) }



此時,我們申請許可權的方法就算編寫完成了,在應用入口,即 EntryAbility.ts 檔案中的

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam)

方法中回撥許可權申請函式:

requestPermissionFromUser(this.context, PERMISSIONS)

其中,PERMISSIONS 定義如下:const PERMISSIONS:Array<Permissions>=['ohos.permission.DISTRIBUTED_DATASYNC']

到此,我們的許可權申請就算完完全全完成啦,當使用者第一次安裝並開啟應用的時候,應用會向使用者透過彈窗形式申請許可權,使用者點選授權即可賦予應用相應的許可權啦~


06、上手分散式資料物件程式碼開發

登入了同一華為帳號的 HarmonyOS 裝置已經預設了進行了組網認證,所以在進行分散式資料物件開發之前無需再進行多裝置組網認證這一階段的開發,開發變得相對簡單了起來。首先,我們們製作一個簡易 UI 介面(UI 介面僅供參考),如下圖所示:


鴻蒙原生應用開發——分散式資料物件


相信對於有 HarmonyOS 開發經驗的小夥伴們來說這樣的 UI 介面製作並不困難,其中紅色圓點、綠色圓點為裝置狀態,當裝置狀態發生改變如下線時,顏色變為紅色,UI 介面程式碼如下:



import router 
from 
'@ohos.router'

import { 
DistributedDeviceManageFunc } 
from 
'../modules/DistributedDeviceManager/DistributedDeviceManagerFunctions'

import 
DistributedObjectFunc 
from 
'../modules/DistributedObject/DistributedObjectFunctions'

import { 
ContinuationDeviceManagerDialog } 
from 
'../view/ContinuationDeviceManagerDialog'

import { 
DistributedDeviceManagerDialog } 
from 
'../view/DistributedDeviceManagerDialog'



AppStorage. SetOrCreate( 'distributedDeviceList', []) AppStorage. SetOrCreate( 'message''分散式資料物件Demo測試') AppStorage. SetOrCreate( 'statusColor''#ff4fc100') AppStorage. SetOrCreate( 'distributedColor''#ffff0000')

@Entry @Component struct  DistributedObjectDemo {    @StorageLink( 'message'messagestring =  ''    @StorageLink( 'statusColor'statusColorstring =  ''    @StorageLink( 'distributedColor'distributedColorstring =  ''    @StorageLink( 'distributedObj'distributedObjDistributedObjectFunc =  new  DistributedObjectFunc()

   @Builder    navigationTitle() {      Row({  space'10vp' }) {        Button({  typeButtonType. Normal }) {          Image($rawfile( 'ic_public_back.svg'))           . size({              width'24vp',              height'24vp'           })       }       . width( '36vp')       . height( '36vp')       . backgroundColor( Color. White)       . borderRadius( '10vp')       . onClick( () => {          DistributedDeviceManageFunc. release()         router. back()       })

       Text( '分散式資料物件測試')         . fontWeight( FontWeight. Bold)         . fontSize( '20vp')        Blank()        Button({  typeButtonType. Normal }) {          Image($rawfile( 'ic_public_connection_filled.svg'))           . size({              width'24vp',              height'24vp'           })       }       . width( '36vp')       . height( '36vp')       . backgroundColor( Color. White)       . borderRadius( '10vp')       . onClick( () => {          this. distributedDeviceManagerDialogController. open()       })     }     . padding( '5vp')     . width( '90%')   }

   build() {      Navigation() {        Column({  space'20vp' }) {          Row({  space'20vp' }) {            Text( `裝置狀態`)             . fontSize( '20vp')             . fontWeight( FontWeight. Bold)            Circle()             . width( '25vp')             . height( '25vp')             . fill( this. statusColor)         }

         Row({  space'20vp' }) {            Text( `對端裝置狀態`)             . fontSize( '20vp')             . fontWeight( FontWeight. Bold)            Circle()             . width( '25vp')             . height( '25vp')             . fill( this. distributedColor)         }

         Text( `SessionID: ${ this.distributedObj.getSessionId()}`)           . fontSize( '20vp')           . fontWeight( FontWeight. Bold)          Text( this. message)           . fontSize( '20vp')           . fontWeight( FontWeight. Bold)           . maxLines( 2)          Button( '儲存分散式資料物件')           . buttonStyles()           . onClick( () => {              this. distributedObj. saveDistributedObject()           })          Button( '修改分散式資料物件')           . buttonStyles()           . onClick( () => {              this. distributedObj. updateDistributedObject()           })          Button( '退出組網')           . buttonStyles()           . onClick( () => {              this. distributedObj. exit()             router. back()           })       }       . width( '100%')     }     . width( '100%')     . height( '100%')     . mode( NavigationMode. Auto)     . titleMode( NavigationTitleMode. Mini)     . hideBackButton( true)     . title( this. navigationTitle())   } }

@Extend( Buttonfunction  buttonStyles() {   . fontSize( '20vp')   . width( '60%')   . height( '50vp') }



現在,我們的頁面製作就完成啦,下面開始重頭戲——分散式資料物件開發流程

1、匯入模組



import distributedObject 
from 
'@ohos.data.distributedDataObject'




2、初始化 distributedObject. DataObject 物件

定義一個 distributedObject. DataObject 型別的變數。


mDistributedObject: distributedObject.DataObject




呼叫 distributedObject. Create()函式建立一個 distributedObject. DataObject 物件,並將其返回給定義的變數 mDistributedObject。



this.mDistributedObject = distributedObject.create(globalThis.context, {
  name: 
'jack',
  age: 
18,
  isVis: 
false
})




在 create()方法中存在兩個引數,context 和 resource,context 的型別為 Context,resource 型別為 object,在這裡我是在 entryAbility.ts 檔案下的 onWindowStageCreate()方法裡面定義了一個全域性變數 globalThis.context。


globalThis.context = 
this.context




3、設定組網 sessionId



this.mDistributedObject.setSessionId(
this.mSessionId)




在 setSessionId()函式中,引數 sessionId 為 string 型別,表示分散式物件組網唯|一識別符號,設定同步的 sessionId,當可信組網中有多個裝置時,多個裝置間的物件如果設定為同一個 sessionId,就能自動同步。

4、開啟裝置狀態監聽


globalThis.
statusCallback = 
(
sessionId: 
string, networkId: 
string, status: 
string) => {
  
AppStorage.
Set(
'message'`組網裝置狀況變更,id:
${sessionId} status:
${status} networkId:
${networkId}`)
  
if (status == 
'online') {
    
AppStorage.
Set(
'distributedColor''#ff4fc100')
  } 
else 
if (status == 
'offline') {
    
AppStorage.
Set(
'distributedColor''#ffff0000')
  }
}

this.
mDistributedObject.
on(
"status", globalThis.
statusCallback)




(sessionId: string, networkId: string, status: string)為 callback 回撥函式返回的值,我們可以使用這些返回值判斷裝置上下線狀態,其中 status 引數返回值為 online 或者 offline,表示裝置對端裝置上下線。

5、開啟分散式資料物件同步監聽


globalThis.
changeCallback = 
(
sessionId: 
string, fields: 
Array<
string>) => {
  
console.
info(
'分散式資料物件發生變化')
  
if (fields != 
null && fields != 
undefined) {
    
AppStorage.
Set(
'message'`data change:
${fields} sessionId:
${sessionId}`)
  }
}

this.
mDistributedObject.
on(
"change", globalThis.
changeCallback)




當同一組網內分散式資料物件發生改變時,同一組網中的所有分散式資料物件同步發生變化,變化後的值為某一分散式資料物件改變後的值(sessionId: string, fields: Array<string>)為 callback 回撥函式返回值,其中,sessionId 為組網唯|一識別符號,field 為分散式資料物件的資料變更列表。

此時此刻,分散式資料物件就基本上開發完成啦。

如果有小夥伴想要修改分散式資料物件的屬性,可以直接修改



// @ts-ignore

this.mDistributedObject.name = 
'lucy'

// @ts-ignore

this.mDistributedObject.age = 
25




注意:根據當前版本 IDE 的編碼外掛情況,不能直接寫 this.mDistributedObject.age = 25,此時我們們需要加上// @ts-ignore 就可以啦。

最後,使用完分散式資料物件後大家要記得釋放資源哦(登出所有監聽,退出組網 sessionId,將分散式資料物件設定為空值)



this.mDistributedObject.off(
"change")

this.mDistributedObject.off(
"status")

this.mDistributedObject.setSessionId()

this.mDistributedObject = 
null

this.mSessionId = 
null




如果有小夥伴有兩部或兩部以上的華為裝置,可以將程式燒錄到裝置中,體驗一下分散式資料物件能力的快樂~


來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70009402/viewspace-2999321/,如需轉載,請註明出處,否則將追究法律責任。

相關文章