HarmonyOS 鸿蒙Next ViewModel中使用Monitor监控属性,退出页面后不被释放,依然能响应数据变化
HarmonyOS 鸿蒙Next ViewModel中使用Monitor监控属性,退出页面后不被释放,依然能响应数据变化
Monitor 问题
场景说明
APP中有对专家进行关注的需求,所有会在不同的页面对专家进行关注,然后结果同步其他页面。这是一个简单的事件分发模型,现在的做法是:
- 创建一个全局单例的关注管理类FollowCommunityUserManager,实例为 followCommunityUserManager,在进行关注的时候调用followCommunityUserManager.follow方法进行关注。
export class FollowCommunityUserEvent extends BaseEvent {
userId: string
hasFollowed: boolean
constructor(userId: string, hasFollowed: boolean) {
super()
this.userId = userId
this.hasFollowed = hasFollowed
}
}
[@ObservedV2](/user/ObservedV2)
export class FollowCommunityUserManager {
@Trace followsFlow?: FollowCommunityUserEvent
async follow(userId?: string, hasFollowed?: boolean) {
HCLog.e("MonitorQuestion", `followCommunityUserManager.follow`)
this.followsFlow = new FollowCommunityUserEvent(userId, !hasFollowed)
ToastUtil.showToast(hasFollowed ? "取消关注成功" : "关注成功")
}
}
export let followCommunityUserManager = new FollowCommunityUserManager()
- 在需要关注的页面中用Monitor对followManager.followsFlow进行监听,触发逻辑处理。
[@ObservedV2](/user/ObservedV2)
export class CommunityDetailsPageVM {
@Trace pageState: CommunityDetailsPageState = new CommunityDetailsPageState()
@Trace followManager = followCommunityUserManager
timestamp: number = Date.now()
[@Monitor](/user/Monitor)('followManager.followsFlow')
onFollowChange(monitor: IMonitor) {
let path = "followManager.followsFlow"
HCLog.e("MonitorQuestion", `CommunityDetailsPageVM ${this.timestamp} ${path} changed from ${monitor.value(path)?.before} to ${JSON.stringify(monitor.value(path)?.now)}`)
// 处理关注事件
}
}
@Entry
@ComponentV2
export struct CommunityDetailsPage {
@Local mVM: CommunityDetailsPageVM = new CommunityDetailsPageVM()
@Local pageState: CommunityDetailsPageState = this.mVM.pageState
@Local followManager: FollowCommunityUserManager = followCommunityUserManager
timestamp: number = Date.now()
[@Monitor](/user/Monitor)('followManager.followsFlow')
onFollowChange(monitor: IMonitor) {
let path = "followManager.followsFlow"
HCLog.e("MonitorQuestion", `CommunityDetailsPage ${this.timestamp} ${path} changed from ${monitor.value(path)?.before} to ${JSON.stringify(monitor.value(path)?.now)}`)
// 处理关注事件
}
aboutToAppear(): void {
HCLog.e("MonitorQuestion", `CommunityDetailsPage ${this.timestamp} aboutToAppear`)
}
aboutToDisappear(): void {
HCLog.e("MonitorQuestion", `CommunityDetailsPage ${this.timestamp} aboutToDisappear`)
}
build() {
NavDestination() {
}
}
}
问题
- 当进入页面->关注->离开页面,重复执行三次后,会出现如下日志:
02-21 11:58:49.468 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPage 1740110329467 aboutToAppear
02-21 11:58:51.855 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, followCommunityUserManager.follow
02-21 11:58:51.856 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPage 1740110329467 followManager.followsFlow changed from undefined to {"userId":206541,"hasFollowed":true}
02-21 11:58:51.856 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPageVM 1740110329467 followManager.followsFlow changed from undefined to {"userId":206541,"hasFollowed":true}
02-21 11:58:54.876 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPage 1740110329467 aboutToDisappear
02-21 11:58:56.581 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPage 1740110336581 aboutToAppear
02-21 11:58:58.412 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, followCommunityUserManager.follow
02-21 11:58:58.413 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPageVM 1740110329467 followManager.followsFlow changed from [object Object] to {"userId":206541,"hasFollowed":false}
02-21 11:58:58.414 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPage 1740110336581 followManager.followsFlow changed from [object Object] to {"userId":206541,"hasFollowed":false}
02-21 11:58:58.414 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPageVM 1740110336581 followManager.followsFlow changed from [object Object] to {"userId":206541,"hasFollowed":false}
02-21 11:59:03.491 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPage 1740110336581 aboutToDisappear
02-21 11:59:05.045 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPage 1740110345045 aboutToAppear
02-21 11:59:07.160 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, followCommunityUserManager.follow
02-21 11:59:07.161 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPageVM 1740110329467 followManager.followsFlow changed from [object Object] to {"userId":206541,"hasFollowed":true}
02-21 11:59:07.161 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPageVM 1740110336581 followManager.followsFlow changed from [object Object] to {"userId":206541,"hasFollowed":true}
02-21 11:59:07.161 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPage 1740110345045 followManager.followsFlow changed from [object Object] to {"userId":206541,"hasFollowed":true}
02-21 11:59:07.162 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPageVM 1740110345044 followManager.followsFlow changed from [object Object] to {"userId":206541,"hasFollowed":true}
02-21 11:59:09.317 28364-28364 A0FFFF/com.net...ttery/lottery com.netease.lottery E MonitorQuestion, CommunityDetailsPage 1740110345045 aboutToDisappear
从日志中发现,ViewModel在页面退出后没有被释放,依然能监听到事件,可能会导致内存的泄漏,请问为什么会出现这种情况?目前没有找到类似生命周期管理的文档说明,假设没有Monitor的情况,ViewModel是否会被释放。
从业务分离的调度说,此处监听放到ViewModel中更加合理,但从目前情况来看不得不放Page中。
理论上Page能做退出后监听不在被响应,他的内部属性ViewModel应该也应该被释放,在《@Monitor装饰器:状态变量修改监听》文档中看到Monitor是可以像我这样在@ObservedV2中使用的。
- 关于单例模式的创建
目前使用export let followCommunityUserManager = new FollowCommunityUserManager()
方式创建,需要同时对外暴露FollowCommunityUserManager和followCommunityUserManager
因为@Local followManager: FollowCommunityUserManager = followCommunityUserManager
必须写明类型。这样在使用中有被错误使用的可能。
参考TS的单例写法export default new FollowCommunityUserManager()
,此时暴露FollowCommunityUserManager即是单例,FollowCommunityUserManager类型不对外暴露,但目前看无法实现,包括AppStorageV2也有类似问题。
- 这样的事件分发是否有问题或隐患?
这是一次简单的事件分发,后续我们会接入websocket,然后全局实时同步上百场赛事的数据,在多页面同步,这样的设计是否会有并发上的问题? 参考Android有Flow做数据队列的缓冲,ArkTS中没有找到类似的功能,像以上这种设计是否可以满足需求?
更多关于HarmonyOS 鸿蒙Next ViewModel中使用Monitor监控属性,退出页面后不被释放,依然能响应数据变化的实战教程也可以访问 https://www.itying.com/category-93-b0.html
问题1,View(UI)和State是分离的,并且可以多个页面使用一份数据,所以页面的显示与隐藏与监控对象是分开的;
问题2,单例可以通过对外暴露类和获取实例化的方法实现的
问题3,关于您所说的隐患问题请放心,鸿蒙支持的更复杂的场景如银行、证券、期货类交易已有很多落地实现;
更多关于HarmonyOS 鸿蒙Next ViewModel中使用Monitor监控属性,退出页面后不被释放,依然能响应数据变化的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next中,ViewModel的生命周期通常与页面的生命周期绑定。当页面退出时,ViewModel应被释放以回收资源。如果在ViewModel中使用Monitor监控属性,且退出页面后ViewModel依然存在并能响应数据变化,可能是由于ViewModel未被正确释放。
ViewModel的生命周期管理依赖于ViewModelStore
,当页面销毁时,ViewModelStore
会调用ViewModel的onCleared()
方法进行清理。如果ViewModel未被释放,可能是以下原因之一:
-
ViewModelStore未正确清理:可能是页面销毁时,
ViewModelStore
未正确调用onCleared()
方法,导致ViewModel未被释放。 -
强引用持有ViewModel:可能存在其他对象对ViewModel的强引用,导致ViewModel无法被垃圾回收机制回收。
-
Monitor未正确释放:Monitor监控属性可能未在
onCleared()
方法中正确释放,导致ViewModel继续持有资源。
确保在页面销毁时,ViewModel能够被正确释放,可以在onCleared()
方法中手动释放Monitor监控属性,并检查是否有其他对象持有ViewModel的引用。
如果问题依然存在,建议检查页面生命周期管理逻辑,确保ViewModel在页面退出时被正确清理。