介紹
本篇Codelab使用設(shè)備管理及分布式鍵值數(shù)據(jù)庫(kù)能力,實(shí)現(xiàn)多設(shè)備之間手寫板應(yīng)用拉起及同步書寫內(nèi)容的功能。操作流程:
- 設(shè)備連接同一無(wú)線網(wǎng)絡(luò),安裝分布式手寫板應(yīng)用。進(jìn)入應(yīng)用,點(diǎn)擊允許使用多設(shè)備協(xié)同,點(diǎn)擊主頁(yè)上查詢?cè)O(shè)備按鈕,顯示附近設(shè)備。
- 選擇設(shè)備確認(rèn),若已建立連接,啟動(dòng)對(duì)方設(shè)備上的手寫板應(yīng)用,否則提示建立連接。輸入PIN碼建立連接后再次點(diǎn)擊查詢?cè)O(shè)備按鈕,選擇設(shè)備提交,啟動(dòng)對(duì)方設(shè)備應(yīng)用。
- 建立連接前繪制的內(nèi)容在啟動(dòng)對(duì)方設(shè)備后同步,此時(shí)設(shè)備上繪制的內(nèi)容會(huì)在另一端同步繪制。
- 點(diǎn)擊撤銷按鈕,兩側(cè)設(shè)備繪制內(nèi)容同步撤銷。
相關(guān)概念
- [設(shè)備管理]:模塊提供分布式設(shè)備管理能力。
- [分布式鍵值數(shù)據(jù)庫(kù)]:分布式鍵值數(shù)據(jù)庫(kù)為應(yīng)用程序提供不同設(shè)備間數(shù)據(jù)庫(kù)的分布式協(xié)同能力。
相關(guān)權(quán)限
本篇Codelab使用了設(shè)備管理及分布式鍵值數(shù)據(jù)庫(kù)能力,需要手動(dòng)替換full-SDK,并在配置文件module.json5文件requestPermissions屬性中添加如下權(quán)限:
- [分布式設(shè)備認(rèn)證組網(wǎng)權(quán)限]:ohos.permission.ACCESS_SERVICE_DM。
- [設(shè)備間的數(shù)據(jù)交換權(quán)限]:ohos.permission.DISTRIBUTED_DATASYNC。
約束與限制
- 本篇Codelab部分能力依賴于系統(tǒng)API,需下載full-SDK并替換DevEco Studio自動(dòng)下載的public-SDK。
- 本篇Codelab使用的部分API僅系統(tǒng)應(yīng)用可用,需要提升應(yīng)用等級(jí)。
環(huán)境搭建
軟件要求
- [DevEco Studio]版本:DevEco Studio 4.0 Beta2。
- OpenHarmony SDK版本:API version 10。
- 鴻蒙指導(dǎo)參考:[
qr23.cn/AKFP8k
]
硬件要求
- 開發(fā)板類型:[潤(rùn)和RK3568開發(fā)板]。
- OpenHarmony系統(tǒng):4.0 Release。
環(huán)境搭建
完成本篇Codelab我們首先要完成開發(fā)環(huán)境的搭建,本示例以RK3568開發(fā)板為例,參照以下步驟進(jìn)行:
- [獲取OpenHarmony系統(tǒng)版本]:標(biāo)準(zhǔn)系統(tǒng)解決方案(二進(jìn)制)。以4.0 Release版本為例:
- 搭建燒錄環(huán)境。
- [完成DevEco Device Tool的安裝]
- [完成RK3568開發(fā)板的燒錄]
- 搭建開發(fā)環(huán)境。
- 開始前請(qǐng)參考[工具準(zhǔn)備],完成DevEco Studio的安裝和開發(fā)環(huán)境配置。
- 開發(fā)環(huán)境配置完成后,請(qǐng)參考[使用工程向?qū)創(chuàng)建工程(模板選擇“Empty Ability”)。
- 工程創(chuàng)建完成后,選擇使用[真機(jī)進(jìn)行調(diào)測(cè)]。
代碼結(jié)構(gòu)解讀
本篇Codelab只對(duì)核心代碼進(jìn)行講解,對(duì)于完整代碼,我們會(huì)在gitee中提供。
├──entry/src/main/ets // 代碼區(qū)
│ ├──common
│ │ ├──constants
│ │ │ └──CommonConstants.ets // 公共常量類
│ │ └──utils
│ │ ├──Logger.ets // 日志打印類
│ │ └──RemoteDeviceUtil.ets // 設(shè)備管理類
│ ├──entryability
│ │ └──EntryAbility.ets // 程序入口類
│ ├──pages
│ │ └──Index.ets // 主界面
│ ├──view
│ │ └──CustomDialogComponent.ets // 自定義彈窗組件類
│ └──viewmodel
│ ├──KvStoreModel.ets // 分布式鍵值數(shù)據(jù)庫(kù)管理類
│ └──Position.ets // 繪制位置信息類
└──entry/src/main/resources // 資源文件目錄
界面設(shè)計(jì)
主界面由導(dǎo)航欄及繪制區(qū)域組成,導(dǎo)航欄包含撤回按鈕及查詢?cè)O(shè)備按鈕。繪制區(qū)域使用Canvas畫布組件展示繪制效果。Index.ets文件完成界面實(shí)現(xiàn),使用Column及Row容器組件進(jìn)行布局。
// Index.ets
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
...
build() {
Column() {
Row() {
// 撤回按鈕
Image($r('app.media.ic_back'))
.width($r('app.float.ic_back_width'))
.height($r('app.float.ic_back_height'))
...
Blank()
// 查找設(shè)備按鈕
Image($r('app.media.ic_hop'))
.width($r('app.float.ic_hop_width'))
.height($r('app.float.ic_hop_height'))
...
}
.width(CommonConstants.FULL_PERCENT)
.height(CommonConstants.TITLE_HEIGHT)
Row() {
// 繪制區(qū)域
Canvas(this.canvasContext)
.width(CommonConstants.FULL_PERCENT)
.height(CommonConstants.FULL_PERCENT)
...
}
...
.width(CommonConstants.FULL_PERCENT)
.layoutWeight(CommonConstants.NUMBER_ONE)
}
.height(CommonConstants.FULL_PERCENT)
.width(CommonConstants.FULL_PERCENT)
}
...
}
分布式組網(wǎng)
準(zhǔn)備分布式環(huán)境
創(chuàng)建設(shè)備管理器。設(shè)備管理器創(chuàng)建完成后注冊(cè)設(shè)備上線離線監(jiān)聽,信任設(shè)備上線離線時(shí)觸發(fā)。執(zhí)行獲取本地設(shè)備信息,獲取信任設(shè)備列表,初始化展示設(shè)備列表等方法。其中deviceManager類需使用full-SDK。
// RemoteDeviceUtil.ets
import deviceManager from '@ohos.distributedHardware.deviceManager';
class RemoteDeviceUtil {
...
async createDeviceManager() {
...
await new Promise((resolve: (value: Object | PromiseLike< Object >) = > void, reject: ((reason?: RejectError) = > void)) = > {
try {
// 創(chuàng)建設(shè)備管理器
deviceManager.createDeviceManager(CommonConstants.BUNDLE_NAME,
(error, value: deviceManager.DeviceManager) = > {
...
this.myDeviceManager = value;
// 注冊(cè)信任設(shè)備上線離線監(jiān)聽
this.registerDeviceStateListener();
// 獲取本地設(shè)備信息
this.getLocalDeviceInfo();
// 獲取信任設(shè)備列表
this.getTrustedDeviceList();
// 初始化展示設(shè)備列表
this.initDeviceList();
resolve(value);
});
} catch (error) {
Logger.error('RemoteDeviceModel',
`createDeviceManager failed, error=${JSON.stringify(error)}`);
}
});
}
...
}
注冊(cè)設(shè)備狀態(tài)監(jiān)聽。已驗(yàn)證設(shè)備上線或有新設(shè)備驗(yàn)證通過(guò)時(shí)狀態(tài)類型為ONLINE,將設(shè)備添加至信任設(shè)備列表。設(shè)備離線時(shí)狀態(tài)類型為OFFLINE,將設(shè)備從信任列表中移除。
// RemoteDeviceUtil.ets
class RemoteDeviceUtil {
...
// 注冊(cè)設(shè)備狀態(tài)改變監(jiān)聽
registerDeviceStateListener(): void {
...
try {
// 注冊(cè)監(jiān)聽
this.myDeviceManager.on('deviceStateChange', (data) = > {
...
switch (data.action) {
// 設(shè)備上線
case deviceManager.DeviceStateChangeAction.ONLINE: {
this.deviceStateChangeActionOnline(data.device);
break;
}
// 設(shè)備離線
case deviceManager.DeviceStateChangeAction.OFFLINE: {
this.deviceStateChangeActionOffline(data.device);
break;
}
...
}
});
} catch (error) {
Logger.error('RemoteDeviceModel',
`registerDeviceStateListener on('deviceStateChange') failed, error=${JSON.stringify(error)}`);
}
}
// 設(shè)備上線,加入信任列表及展示列表
deviceStateChangeActionOnline(device: deviceManager.DeviceInfo): void {
this.trustedDeviceList[this.trustedDeviceList.length] = device;
this.addToDeviceList(device);
}
// 設(shè)備下線,將設(shè)備移出信任列表和展示列表
deviceStateChangeActionOffline(device: deviceManager.DeviceInfo): void {
let list: deviceManager.DeviceInfo[] = [];
for (let i: number = 0; i < this.trustedDeviceList.length; i++) {
if (this.trustedDeviceList[i].networkId !== device.networkId) {
list.push(this.trustedDeviceList[i]);
continue;
}
}
this.deleteFromDeviceList(device);
this.trustedDeviceList = list;
}
...
}
建立分布式連接
點(diǎn)擊主界面的查詢?cè)O(shè)備按鈕,執(zhí)行發(fā)現(xiàn)設(shè)備方法,注冊(cè)設(shè)備發(fā)現(xiàn)監(jiān)聽任務(wù),同時(shí)拉起彈窗展示設(shè)備列表。當(dāng)彈窗關(guān)閉時(shí),執(zhí)行停止發(fā)現(xiàn)設(shè)備方法,注銷監(jiān)聽任務(wù)。
// RemoteDeviceUtil.ets
class RemoteDeviceUtil {
...
// 處理新發(fā)現(xiàn)的設(shè)備
deviceFound(data: DeviceInfoInterface): void {
for (let i: number = 0; i < this.discoverList.length; i++) {
if (this.discoverList[i].deviceId === data.device.deviceId) {
Logger.info('RemoteDeviceModel', `deviceFound device exist=${JSON.stringify(data)}`);
return;
}
}
this.discoverList[this.discoverList.length] = data.device;
this.addToDeviceList(data.device);
}
startDeviceDiscovery(): void {
...
try {
// 注冊(cè)發(fā)現(xiàn)設(shè)備監(jiān)聽
this.myDeviceManager.on('deviceFound', (data) = > {
...
// 處理發(fā)現(xiàn)的設(shè)備
this.deviceFound(data);
});
...
let info: deviceManager.SubscribeInfo = {
subscribeId: this.subscribeId,
mode: CommonConstants.SUBSCRIBE_MODE,
medium: CommonConstants.SUBSCRIBE_MEDIUM,
freq: CommonConstants.SUBSCRIBE_FREQ,
isSameAccount: false,
isWakeRemote: true,
capability: CommonConstants.SUBSCRIBE_CAPABILITY
};
// 發(fā)現(xiàn)周邊設(shè)備
this.myDeviceManager.startDeviceDiscovery(info);
} catch (error) {
Logger.error('RemoteDeviceModel',
`startDeviceDiscovery failed error=${JSON.stringify(error)}`);
}
}
// 停止發(fā)現(xiàn)設(shè)備
stopDeviceDiscovery(): void {
...
try {
// 停止發(fā)現(xiàn)設(shè)備
this.myDeviceManager.stopDeviceDiscovery(this.subscribeId);
// 注銷監(jiān)聽任務(wù)
this.myDeviceManager.off('deviceFound');
this.myDeviceManager.off('discoverFail');
} catch (error) {
Logger.error('RemoteDeviceModel',
`stopDeviceDiscovery failed error=${JSON.stringify(error)}`);
}
}
...
}
選擇彈窗內(nèi)的設(shè)備項(xiàng)提交后,執(zhí)行設(shè)備驗(yàn)證。
- 若設(shè)備在信任設(shè)備列表,執(zhí)行startAbility()方法啟動(dòng)連接設(shè)備上的應(yīng)用,將當(dāng)前的繪制信息作為參數(shù)發(fā)送至連接設(shè)備。
- 若設(shè)備不是信任設(shè)備,執(zhí)行authenticateDevice()方法啟動(dòng)驗(yàn)證。此時(shí)連接設(shè)備提示是否接受,接收連接后連接設(shè)備展示PIN碼,本地設(shè)備輸入PIN碼確認(rèn)后連接成功。再次點(diǎn)擊查詢?cè)O(shè)備按鈕,選擇已連接設(shè)備,點(diǎn)擊確認(rèn)啟動(dòng)連接設(shè)備上的應(yīng)用。
// RemoteDeviceUtil.ets
class RemoteDeviceUtil {
...
// 設(shè)備驗(yàn)證
authenticateDevice(
context: common.UIAbilityContext,
device: deviceManager.DeviceInfo,
positionList: Position[]
): void {
// 設(shè)備為信任設(shè)備,啟動(dòng)連接設(shè)備上的應(yīng)用
let tmpList = this.trustedDeviceList.filter((item: deviceManager.DeviceInfo) = > device.deviceId === item.deviceId);
if (tmpList.length > 0) {
this.startAbility(context, device, positionList);
return;
}
...
try {
// 執(zhí)行設(shè)備認(rèn)證,啟動(dòng)驗(yàn)證相關(guān)彈窗,接受信任,顯示PIN碼,輸入PIN碼等
this.myDeviceManager.authenticateDevice(device, authParam, (err) = > {
...
})
} catch (error) {
Logger.error('RemoteDeviceModel',
`authenticateDevice failed error=${JSON.stringify(error)}`);
}
}
// 啟動(dòng)連接設(shè)備上的應(yīng)用
startAbility(context: common.UIAbilityContext, device: deviceManager.DeviceInfo, positionList: Position[]): void {
...
// 啟動(dòng)連接設(shè)備上的應(yīng)用
context.startAbility(wantValue).then(() = > {
Logger.info('RemoteDeviceModel', `startAbility finished wantValue=${JSON.stringify(wantValue)}`);
}).catch((error: Error) = > {
Logger.error('RemoteDeviceModel', `startAbility failed, error=${JSON.stringify(error)}`);
})
}
...
}
資源釋放
程序關(guān)閉時(shí),注銷設(shè)備狀態(tài)監(jiān)聽任務(wù),并釋放DeviceManager實(shí)例。
// RemoteDeviceUtil.ets
class RemoteDeviceUtil {
...
// 注銷監(jiān)聽任務(wù)
unregisterDeviceListCallback(): void {
...
try {
// 注銷設(shè)備狀態(tài)監(jiān)聽
this.myDeviceManager.off('deviceStateChange');
// 釋放DeviceManager實(shí)例
this.myDeviceManager.release();
} catch (err) {
Logger.error('RemoteDeviceModel',
`unregisterDeviceListCallback stopDeviceDiscovery failed, error=${JSON.stringify(err)}`);
}
}
...
}
繪制功能
Canvas組件區(qū)域監(jiān)聽觸摸事件,按照按下、移動(dòng)、抬起等觸摸事件,記錄繪制的起點(diǎn)、中間點(diǎn)以及終點(diǎn)。觸摸事件觸發(fā)時(shí),使用CanvasRenderingContext2D對(duì)象的繪制方法根據(jù)位置信息進(jìn)行繪制。繪制結(jié)束后,將當(dāng)前位置信息列表存入分布式鍵值數(shù)據(jù)庫(kù)。
// Index.ets
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
...
build() {
Column() {
...
Row() {
Canvas(this.canvasContext)
...
}
.onTouch((event: TouchEvent) = > {
this.onTouchEvent(event);
})
...
}
...
}
// 繪制事件
onTouchEvent(event: TouchEvent): void {
let positionX: number = event.touches[0].x;
let positionY: number = event.touches[0].y;
switch (event.type) {
// 手指按下
case TouchType.Down: {
this.canvasContext.beginPath();
this.canvasContext.lineWidth = CommonConstants.CANVAS_LINE_WIDTH;
this.canvasContext.lineJoin = CommonConstants.CANVAS_LINE_JOIN;
this.canvasContext.moveTo(positionX, positionY);
this.pushData(true, false, positionX, positionY);
break;
}
// 手指移動(dòng)
case TouchType.Move: {
this.canvasContext.lineTo(positionX, positionY);
this.pushData(false, false, positionX, positionY);
break;
}
// 手指抬起
case TouchType.Up: {
this.canvasContext.lineTo(positionX, positionY);
this.canvasContext.stroke();
this.pushData(false, true, positionX, positionY);
break;
}
default: {
break;
}
}
}
pushData(isFirstPosition: boolean, isEndPosition: boolean, positionX: number, positionY: number): void {
let position = new Position(isFirstPosition, isEndPosition, positionX, positionY);
// 存入位置信息列表
this.positionList.push(position);
if (position.isEndPosition) {
// 當(dāng)前位置為終點(diǎn)時(shí),將位置信息列表存入分布式鍵值數(shù)據(jù)庫(kù)
this.kvStoreModel.put(CommonConstants.CHANGE_POSITION, JSON.stringify(this.positionList));
}
}
...
}
點(diǎn)擊撤銷按鈕時(shí),從位置列表中后序遍歷移除位置信息,直到找到軌跡的初始位置,完成移除上一次繪制的軌跡。移除完成后將位置信息列表存入分布式鍵值數(shù)據(jù)庫(kù)中。執(zhí)行redraw()方法,清空畫板上的內(nèi)容,遍歷位置信息列表,重新繪制。
// Index.ets
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
...
@LocalStorageProp('positionList') positionList: Position[] = [];
...
build() {
Column() {
Row() {
// 撤銷按鈕
Image($r('app.media.ic_back'))
.width($r('app.float.ic_back_width'))
.height($r('app.float.ic_back_height'))
.margin({ left: CommonConstants.ICON_MARGIN_LEFT })
.onClick(() = > {
this.goBack();
})
...
}
.width(CommonConstants.FULL_PERCENT)
.height(CommonConstants.TITLE_HEIGHT)
...
}
...
redraw(): void {
// 刪除畫布內(nèi)的繪制內(nèi)容
this.canvasContext.clearRect(0, 0, this.canvasContext.width, this.canvasContext.height);
// 使用當(dāng)前記錄的位置信息,重新繪制
this.positionList.forEach((position) = > {
...
if (position.isFirstPosition) {
this.canvasContext.beginPath();
this.canvasContext.lineWidth = CommonConstants.CANVAS_LINE_WIDTH;
this.canvasContext.lineJoin = CommonConstants.CANVAS_LINE_JOIN;
this.canvasContext.moveTo(position.positionX, position.positionY);
} else {
this.canvasContext.lineTo(position.positionX, position.positionY);
if (position.isEndPosition) {
this.canvasContext.stroke();
}
}
});
}
// 撤回上一筆繪制
goBack(): void {
if (this.positionList.length === 0) {
return;
}
// 移除位置信息直到位置起始位置
for (let i: number = this.positionList.length - 1; i >= 0; i--) {
let position: Position | undefined = this.positionList.pop();
if (position !== undefined && position.isFirstPosition) {
break;
}
}
this.redraw();
this.kvStoreModel.put(CommonConstants.CHANGE_POSITION, JSON.stringify(this.positionList));
}
...
}
分布式鍵值數(shù)據(jù)庫(kù)
使用分布式鍵值數(shù)據(jù)庫(kù)需申請(qǐng)數(shù)據(jù)交換權(quán)限:ohos.permission.DISTRIBUTED_DATASYNC。
應(yīng)用啟動(dòng)時(shí)創(chuàng)建分布式鍵值數(shù)據(jù)庫(kù),設(shè)置數(shù)據(jù)庫(kù)數(shù)據(jù)改變監(jiān)聽。數(shù)據(jù)改變時(shí)執(zhí)行回調(diào),獲取插入或更新數(shù)據(jù)列表,遍歷列表,匹配位置信息列表的設(shè)置key,更新位置列表后重新繪制。
// Index.ets
...
import KvStoreModel from '../viewmodel/KvStoreModel';
...
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
...
private kvStoreModel: KvStoreModel = new KvStoreModel();
...
aboutToAppear() {
...
this.createKVStore();
}
...
createKVStore(): void {
// 創(chuàng)建分布式鍵值數(shù)據(jù)庫(kù)
this.kvStoreModel.createKvStore(this.context, (data: distributedKVStore.ChangeNotification) = > {
// 使用分布式鍵值數(shù)據(jù)庫(kù)內(nèi)的內(nèi)容重置位置信息列表
this.positionList = [];
let entries: distributedKVStore.Entry[] = data.insertEntries.length > 0 ? data.insertEntries : data.updateEntries;
entries.forEach((entry: distributedKVStore.Entry) = > {
if (CommonConstants.CHANGE_POSITION === entry.key) {
this.positionList = JSON.parse((entry.value.value) as string);
// 位置信息列表更新后,重新繪制
this.redraw();
}
});
});
}
...
}
創(chuàng)建分布式鍵值數(shù)據(jù)庫(kù)。設(shè)置數(shù)據(jù)庫(kù)類型為KVStoreType.SINGLE_VERSION單版本數(shù)據(jù)庫(kù),其他配置參考[創(chuàng)建數(shù)據(jù)庫(kù)配置信息]。創(chuàng)建數(shù)據(jù)庫(kù)成功后,調(diào)用enableSync()方法開啟同步,調(diào)用setDataChangeListener()方法訂閱數(shù)據(jù)變更通知。
// KvStoreModel.ets
export default class KvStoreModel {
...
kvStore?: distributedKVStore.SingleKVStore;
...
createKvStore(
context: common.UIAbilityContext,
callback: (data: distributedKVStore.ChangeNotification) = > void
): void {
...
try {
// 創(chuàng)建一個(gè)KVManager對(duì)象實(shí)例,用于管理數(shù)據(jù)庫(kù)對(duì)象
this.kvManager = distributedKVStore.createKVManager(config);
} catch (error) {
Logger.error('KvStoreModel',
`createKvStore createKVManager failed, err=${JSON.stringify(error)}`);
return;
}
// 創(chuàng)建數(shù)據(jù)庫(kù)的配置信息
let options: distributedKVStore.Options = {
...
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION
...
};
// 獲取分布式鍵值數(shù)據(jù)庫(kù)
this.kvManager.getKVStore(CommonConstants.KVSTORE_ID, options).then((store: distributedKVStore.SingleKVStore) = > {
...
this.kvStore = store;
// 開啟同步
this.kvStore.enableSync(true).then(() = > {
Logger.info('KvStoreModel', 'createKvStore enableSync success');
}).catch((error: Error) = > {
Logger.error('KvStoreModel',
`createKvStore enableSync fail, error=${JSON.stringify(error)}`);
});
this.setDataChangeListener(callback);
}).catch((error: Error) = > {
Logger.error('getKVStore',
`createKvStore getKVStore failed, error=${JSON.stringify(error)}`);
})
}
...
}
訂閱數(shù)據(jù)變更通知。創(chuàng)建分布式鍵值數(shù)據(jù)庫(kù),設(shè)置數(shù)據(jù)變更訂閱,訂閱類型為全部,當(dāng)更新數(shù)據(jù)集或插入數(shù)據(jù)集大于0時(shí),執(zhí)行傳入的callback()方法。
// KvStoreModel.ets
export default class KvStoreModel {
...
kvStore?: distributedKVStore.SingleKVStore;
...
setDataChangeListener(callback: (data: distributedKVStore.ChangeNotification) = > void): void {
...
try {
// 訂閱數(shù)據(jù)變更通知
this.kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL,
(data: distributedKVStore.ChangeNotification) = > {
if ((data.updateEntries.length > 0) || (data.insertEntries.length > 0)) {
callback(data);
}
});
} catch (error) {
Logger.error('KvStoreModel',
`setDataChangeListener on('dataChange') failed, err=${JSON.stringify(error)}`);
}
}
...
}
應(yīng)用退出時(shí),分布式鍵值數(shù)據(jù)庫(kù)取消數(shù)據(jù)改變監(jiān)聽。
// Index.ets
...
import KvStoreModel from '../viewmodel/KvStoreModel';
...
let storage = LocalStorage.getShared();
@Entry(storage)
@Component
struct Index {
...
private kvStoreModel: KvStoreModel = new KvStoreModel();
...
aboutToDisappear() {
this.kvStoreModel.removeDataChangeListener();
}
...
}
// KvStoreModel.ets
export default class KvStoreModel {
...
kvStore?: distributedKVStore.SingleKVStore;
...
removeDataChangeListener(): void {
...
try {
// 取消數(shù)據(jù)改變監(jiān)聽
this.kvStore.off('dataChange');
} catch (error) {
Logger.error('KvStoreModel',
`removeDataChangeListener off('dataChange') failed, err=${JSON.stringify(error)}`);
}
}
...
}
審核編輯 黃宇
-
分布式
+關(guān)注
關(guān)注
1文章
899瀏覽量
74509 -
HarmonyOS
+關(guān)注
關(guān)注
79文章
1975瀏覽量
30201 -
OpenHarmony
+關(guān)注
關(guān)注
25文章
3722瀏覽量
16320 -
鴻蒙OS
+關(guān)注
關(guān)注
0文章
188瀏覽量
4396
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論