粘性事件:HarmonyOS APP开发中延迟事件处理
【摘要】 粘性事件:延迟事件处理“先订阅后发布"还是"先发布后订阅”?粘性事件让顺序不再重要 一、背景与动机:为什么需要粘性事件? 1.1 订阅顺序问题考虑这个场景:你的应用启动时需要获取当前网络状态,但网络状态变化事件已经发布过了。// ❌ 普通事件:错过了就没了// 1. 系统启动,网络连接,发布事件publish('NETWORK_CONNECTED') // 时刻T1// 2. 应用启动,...
粘性事件:延迟事件处理
“先订阅后发布"还是"先发布后订阅”?粘性事件让顺序不再重要
一、背景与动机:为什么需要粘性事件?
1.1 订阅顺序问题
考虑这个场景:你的应用启动时需要获取当前网络状态,但网络状态变化事件已经发布过了。
// ❌ 普通事件:错过了就没了
// 1. 系统启动,网络连接,发布事件
publish('NETWORK_CONNECTED') // 时刻T1
// 2. 应用启动,订阅事件(晚了!)
subscribe('NETWORK_CONNECTED', callback) // 时刻T2 > T1
// 问题:callback永远不会被调用,因为事件已经过去了
**粘性事件(Sticky Event)**解决了这个问题:事件会被缓存,新订阅者订阅时立即收到最近的事件。
// ✅ 粘性事件:不会错过
// 1. 系统启动,网络连接,发布粘性事件
publishSticky('NETWORK_CONNECTED') // 事件被缓存
// 2. 应用启动,订阅粘性事件
subscribeSticky('NETWORK_CONNECTED', callback) // 立即收到缓存的事件!
// callback会立即被调用,收到最新的网络状态
1.2 粘性事件的特点

1.3 适用场景
| 场景 | 为什么用粘性事件 |
|---|---|
| 获取当前状态 | 订阅时立即获取最新状态,无需查询 |
| 配置同步 | 新组件启动时获取当前配置 |
| 位置信息 | 订阅时获取最后已知位置 |
| 网络状态 | 启动时立即知道是否联网 |
| 用户登录状态 | 新页面立即知道登录状态 |
二、核心原理:粘性事件机制
2.1 粘性事件存储
sequenceDiagram
participant Publisher as 发布者
participant Manager as CommonEventManager
participant Cache as 粘性事件缓存
participant Subscriber1 as 订阅者1
participant Subscriber2 as 订阅者2(新)
Note over Publisher: 发布粘性事件
Publisher->>Manager: publish(event, data, sticky=true)
Manager->>Cache: 保存事件
Cache-->>Cache: 覆盖旧事件
Note over Subscriber1: 已订阅
Manager->>Subscriber1: 回调
Note over Subscriber2: 新订阅(事件已发布)
Subscriber2->>Manager: subscribe(event, getSticky=true)
Manager->>Cache: 查找缓存事件
Cache-->>Manager: 返回缓存数据
Manager->>Subscriber2: 立即回调
classDef primary fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
classDef warning fill:#fff3e0,stroke:#e65100,stroke-width:2px
classDef success fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
class Publisher,Subscriber1,Subscriber2 primary
class Manager warning
class Cache success
2.2 粘性事件 vs 普通事件
// 内部实现对比(伪代码)
// 普通事件发布
publishNormal(event: string, data: unknown) {
// 直接分发给当前订阅者
const subscribers = this.getSubscribers(event)
for (const sub of subscribers) {
sub.callback(data)
}
// 事件不保存,分发完就没了
}
// 粘性事件发布
publishSticky(event: string, data: unknown) {
// 1. 保存到缓存(覆盖旧的)
this.stickyCache.set(event, data)
// 2. 分发给当前订阅者
const subscribers = this.getSubscribers(event)
for (const sub of subscribers) {
sub.callback(data)
}
}
// 普通事件订阅
subscribeNormal(event: string, callback: Function) {
// 只添加到订阅列表
this.addSubscriber(event, callback)
// 不会立即回调
}
// 粘性事件订阅
subscribeSticky(event: string, callback: Function) {
// 1. 添加到订阅列表
this.addSubscriber(event, callback)
// 2. 如果有缓存事件,立即回调
if (this.stickyCache.has(event)) {
const cachedData = this.stickyCache.get(event)
callback(cachedData) // 立即触发
}
}
2.3 粘性事件生命周期

三、代码实战:粘性事件应用
3.1 发布粘性事件
// StickyEventPublisher.ets
import { commonEventManager, CommonEventPublishData } from '@kit.BasicServicesKit'
import hilog from '@ohos.hilog'
const TAG = 'StickyPublisher'
const DOMAIN = 0xFF00
/**
* 粘性事件发布者
*/
export class StickyEventPublisher {
/**
* 发布配置变更粘性事件
* 新订阅者会立即收到当前配置
*/
static async publishConfig(config: AppConfig): Promise<void> {
const options: CommonEventPublishData = {
event: 'com.example.CONFIG_CHANGED',
data: JSON.stringify(config),
// 关键:标记为粘性事件
isSticky: true,
parameters: {
theme: config.theme,
language: config.language,
fontSize: config.fontSize
}
}
try {
await commonEventManager.publish(options)
hilog.info(DOMAIN, TAG, `Config published: ${JSON.stringify(config)}`)
} catch (err) {
hilog.error(DOMAIN, TAG, `Publish failed: ${err.message}`)
}
}
/**
* 发布用户登录状态粘性事件
*/
static async publishLoginState(userInfo: UserInfo | null): Promise<void> {
const options: CommonEventPublishData = {
event: 'com.example.LOGIN_STATE',
data: userInfo ? JSON.stringify(userInfo) : '',
isSticky: true,
parameters: {
isLoggedIn: userInfo !== null,
userId: userInfo?.id ?? 0,
userName: userInfo?.name ?? ''
}
}
try {
await commonEventManager.publish(options)
hilog.info(DOMAIN, TAG, `Login state published: ${userInfo ? 'logged in' : 'logged out'}`)
} catch (err) {
hilog.error(DOMAIN, TAG, `Publish failed: ${err.message}`)
}
}
/**
* 发布网络状态粘性事件
*/
static async publishNetworkState(status: NetworkStatus): Promise<void> {
const options: CommonEventPublishData = {
event: 'com.example.NETWORK_STATE',
data: JSON.stringify(status),
isSticky: true,
parameters: {
isConnected: status.isConnected,
type: status.type,
wifiName: status.wifiName ?? ''
}
}
try {
await commonEventManager.publish(options)
hilog.info(DOMAIN, TAG, `Network state published`)
} catch (err) {
hilog.error(DOMAIN, TAG, `Publish failed: ${err.message}`)
}
}
/**
* 发布位置信息粘性事件
*/
static async publishLocation(location: Location | null): Promise<void> {
const options: CommonEventPublishData = {
event: 'com.example.LOCATION_UPDATE',
data: location ? JSON.stringify(location) : '',
isSticky: true,
parameters: {
hasLocation: location !== null,
latitude: location?.latitude ?? 0,
longitude: location?.longitude ?? 0,
timestamp: Date.now()
}
}
try {
await commonEventManager.publish(options)
hilog.info(DOMAIN, TAG, `Location published`)
} catch (err) {
hilog.error(DOMAIN, TAG, `Publish failed: ${err.message}`)
}
}
}
/**
* 应用配置
*/
interface AppConfig {
theme: 'light' | 'dark'
language: string
fontSize: number
}
/**
* 用户信息
*/
interface UserInfo {
id: number
name: string
avatar?: string
}
/**
* 网络状态
*/
interface NetworkStatus {
isConnected: boolean
type: string
wifiName?: string
}
/**
* 位置信息
*/
interface Location {
latitude: number
longitude: number
accuracy?: number
}
3.2 订阅粘性事件
// StickyEventSubscriber.ets
import { commonEventManager, CommonEventSubscribeInfo, CommonEventData } from '@kit.BasicServicesKit'
import { EventHandler, EventRunner } from '@kit.BasicServicesKit'
import hilog from '@ohos.hilog'
const TAG = 'StickySubscriber'
const DOMAIN = 0xFF00
/**
* 粘性事件订阅者
*/
export class StickyEventSubscriber {
private subscriber: commonEventManager.CommonEventSubscriber = null
private mainHandler: EventHandler
// 当前状态(从粘性事件获取)
private currentConfig: AppConfig | null = null
private isLoggedIn: boolean = false
private currentUser: UserInfo | null = null
private networkStatus: NetworkStatus | null = null
// 状态变化回调
private onConfigChange: ((config: AppConfig) => void) | null = null
private onLoginStateChange: ((isLoggedIn: boolean, user: UserInfo | null) => void) | null = null
private onNetworkChange: ((status: NetworkStatus) => void) | null = null
constructor() {
// 创建主线程Handler
const mainRunner = EventRunner.getMainEventRunner()
this.mainHandler = new EventHandler(mainRunner)
}
/**
* 设置配置变化回调
*/
setOnConfigChange(callback: (config: AppConfig) => void): void {
this.onConfigChange = callback
}
/**
* 设置登录状态变化回调
*/
setOnLoginStateChange(callback: (isLoggedIn: boolean, user: UserInfo | null) => void): void {
this.onLoginStateChange = callback
}
/**
* 设置网络状态变化回调
*/
setOnNetworkChange(callback: (status: NetworkStatus) => void): void {
this.onNetworkChange = callback
}
/**
* 开始订阅粘性事件
*/
async startSubscribing(): Promise<void> {
const subscribeInfo: CommonEventSubscribeInfo = {
events: [
'com.example.CONFIG_CHANGED',
'com.example.LOGIN_STATE',
'com.example.NETWORK_STATE',
'com.example.LOCATION_UPDATE'
],
// 关键:请求获取粘性事件
getStickEvent: true
}
try {
this.subscriber = await commonEventManager.createSubscriber(subscribeInfo)
this.subscriber.subscribe((err, data: CommonEventData) => {
if (err) {
hilog.error(DOMAIN, TAG, `Subscribe error: ${err.message}`)
return
}
// 转发到主线程处理
this.mainHandler.sendEvent({
eventId: 1,
param: data
})
})
// 设置主线程事件处理
this.mainHandler.setEventHandler((eventId, param) => {
if (eventId === 1) {
this.handleStickyEvent(param as CommonEventData)
}
})
hilog.info(DOMAIN, TAG, 'Sticky event subscriber started')
hilog.info(DOMAIN, TAG, 'Will receive cached events immediately')
} catch (err) {
hilog.error(DOMAIN, TAG, `Start failed: ${err.message}`)
}
}
/**
* 处理粘性事件
*/
private handleStickyEvent(data: CommonEventData): void {
hilog.info(DOMAIN, TAG, `Received sticky event: ${data.event}`)
switch (data.event) {
case 'com.example.CONFIG_CHANGED':
this.handleConfigChange(data)
break
case 'com.example.LOGIN_STATE':
this.handleLoginStateChange(data)
break
case 'com.example.NETWORK_STATE':
this.handleNetworkStateChange(data)
break
case 'com.example.LOCATION_UPDATE':
this.handleLocationUpdate(data)
break
}
}
/**
* 处理配置变化
*/
private handleConfigChange(data: CommonEventData): void {
try {
// 解析配置数据
this.currentConfig = JSON.parse(data.data) as AppConfig
hilog.info(DOMAIN, TAG, `Config: theme=${this.currentConfig.theme}, lang=${this.currentConfig.language}`)
// 触发回调
if (this.onConfigChange) {
this.onConfigChange(this.currentConfig)
}
} catch (err) {
hilog.error(DOMAIN, TAG, `Parse config failed: ${err.message}`)
}
}
/**
* 处理登录状态变化
*/
private handleLoginStateChange(data: CommonEventData): void {
const params = data.parameters
this.isLoggedIn = params?.isLoggedIn ?? false
if (this.isLoggedIn && data.data) {
try {
this.currentUser = JSON.parse(data.data) as UserInfo
} catch (err) {
this.currentUser = null
}
} else {
this.currentUser = null
}
hilog.info(DOMAIN, TAG, `Login state: ${this.isLoggedIn}`)
if (this.onLoginStateChange) {
this.onLoginStateChange(this.isLoggedIn, this.currentUser)
}
}
/**
* 处理网络状态变化
*/
private handleNetworkStateChange(data: CommonEventData): void {
try {
this.networkStatus = JSON.parse(data.data) as NetworkStatus
hilog.info(DOMAIN, TAG, `Network: ${this.networkStatus.isConnected ? 'connected' : 'disconnected'}`)
if (this.onNetworkChange) {
this.onNetworkChange(this.networkStatus)
}
} catch (err) {
hilog.error(DOMAIN, TAG, `Parse network state failed: ${err.message}`)
}
}
/**
* 处理位置更新
*/
private handleLocationUpdate(data: CommonEventData): void {
const params = data.parameters
const hasLocation = params?.hasLocation ?? false
if (hasLocation && data.data) {
try {
const location = JSON.parse(data.data) as Location
hilog.info(DOMAIN, TAG, `Location: ${location.latitude}, ${location.longitude}`)
} catch (err) {
hilog.error(DOMAIN, TAG, `Parse location failed: ${err.message}`)
}
}
}
/**
* 获取当前配置(从粘性事件获取)
*/
getCurrentConfig(): AppConfig | null {
return this.currentConfig
}
/**
* 获取登录状态
*/
getLoginState(): { isLoggedIn: boolean, user: UserInfo | null } {
return {
isLoggedIn: this.isLoggedIn,
user: this.currentUser
}
}
/**
* 获取网络状态
*/
getNetworkStatus(): NetworkStatus | null {
return this.networkStatus
}
/**
* 停止订阅
*/
async stopSubscribing(): Promise<void> {
if (this.subscriber) {
try {
await this.subscriber.unsubscribe()
hilog.info(DOMAIN, TAG, 'Sticky event subscriber stopped')
} catch (err) {
hilog.error(DOMAIN, TAG, `Stop failed: ${err.message}`)
}
}
}
}
3.3 实战:应用配置同步
// ConfigSync.ets
import { StickyEventPublisher } from './StickyEventPublisher'
import { StickyEventSubscriber } from './StickyEventSubscriber'
import hilog from '@ohos.hilog'
const TAG = 'ConfigSync'
const DOMAIN = 0xFF00
/**
* 应用配置管理器
* 使用粘性事件实现配置同步
*/
export class ConfigManager {
private static instance: ConfigManager
private subscriber: StickyEventSubscriber
// 当前配置
private config: AppConfig = {
theme: 'light',
language: 'zh',
fontSize: 14
}
// 配置变化回调
private onConfigUpdate: ((config: AppConfig) => void) | null = null
private constructor() {
this.subscriber = new StickyEventSubscriber()
}
/**
* 获取单例
*/
static getInstance(): ConfigManager {
if (!ConfigManager.instance) {
ConfigManager.instance = new ConfigManager()
}
return ConfigManager.instance
}
/**
* 初始化
*/
async initialize(): Promise<void> {
// 设置配置变化回调
this.subscriber.setOnConfigChange((config) => {
this.config = config
hilog.info(DOMAIN, TAG, `Config updated: ${JSON.stringify(config)}`)
if (this.onConfigUpdate) {
this.onConfigUpdate(config)
}
})
// 开始订阅(会立即收到缓存的配置)
await this.subscriber.startSubscribing()
hilog.info(DOMAIN, TAG, 'ConfigManager initialized')
}
/**
* 设置配置更新回调
*/
setOnConfigUpdate(callback: (config: AppConfig) => void): void {
this.onConfigUpdate = callback
}
/**
* 获取当前配置
*/
getConfig(): AppConfig {
return { ...this.config }
}
/**
* 更新配置
*/
async updateConfig(newConfig: Partial<AppConfig>): Promise<void> {
// 合并配置
this.config = { ...this.config, ...newConfig }
// 发布粘性事件(通知所有订阅者)
await StickyEventPublisher.publishConfig(this.config)
hilog.info(DOMAIN, TAG, `Config updated and published`)
}
/**
* 切换主题
*/
async toggleTheme(): Promise<void> {
const newTheme = this.config.theme === 'light' ? 'dark' : 'light'
await this.updateConfig({ theme: newTheme })
}
/**
* 切换语言
*/
async changeLanguage(language: string): Promise<void> {
await this.updateConfig({ language })
}
/**
* 调整字体大小
*/
async adjustFontSize(delta: number): Promise<void> {
const newSize = Math.max(12, Math.min(20, this.config.fontSize + delta))
await this.updateConfig({ fontSize: newSize })
}
/**
* 销毁
*/
async destroy(): Promise<void> {
await this.subscriber.stopSubscribing()
}
}
/**
* 在UI组件中使用
*/
@Entry
@Component
struct ConfigPage {
@State theme: string = 'light'
@State language: string = 'zh'
@State fontSize: number = 14
private configManager: ConfigManager = ConfigManager.getInstance()
async aboutToAppear() {
// 设置配置更新回调
this.configManager.setOnConfigUpdate((config) => {
// 更新UI(在主线程)
this.theme = config.theme
this.language = config.language
this.fontSize = config.fontSize
})
// 初始化(会立即获取当前配置)
await this.configManager.initialize()
// 获取初始配置
const config = this.configManager.getConfig()
this.theme = config.theme
this.language = config.language
this.fontSize = config.fontSize
}
build() {
Column() {
Text('应用配置')
.fontSize(24)
.fontWeight(FontWeight.Bold)
// 主题切换
Row() {
Text('主题:')
.width(80)
Toggle({ type: ToggleType.Switch, isOn: this.theme === 'dark' })
.onChange((isOn) => {
this.configManager.toggleTheme()
})
}
.width('100%')
.margin({ top: 20 })
// 语言选择
Row() {
Text('语言:')
.width(80)
Select([
{ value: '中文' },
{ value: 'English' }
])
.selected(this.language === 'zh' ? 0 : 1)
.onSelect((index) => {
this.configManager.changeLanguage(index === 0 ? 'zh' : 'en')
})
}
.width('100%')
.margin({ top: 20 })
// 字体大小
Row() {
Text('字体大小:')
.width(80)
Text(`${this.fontSize}`)
.width(50)
Button('-')
.onClick(() => {
this.configManager.adjustFontSize(-1)
})
Button('+')
.onClick(() => {
this.configManager.adjustFontSize(1)
})
}
.width('100%')
.margin({ top: 20 })
}
.width('100%')
.height('100%')
.padding(20)
}
}
3.4 实战:多页面状态同步
// MultiPageSync.ets
import { StickyEventPublisher } from './StickyEventPublisher'
import { StickyEventSubscriber } from './StickyEventSubscriber'
import router from '@ohos.router'
import hilog from '@ohos.hilog'
const TAG = 'MultiPageSync'
const DOMAIN = 0xFF00
/**
* 购物车状态
*/
interface CartState {
items: CartItem[]
totalCount: number
totalPrice: number
}
interface CartItem {
id: string
name: string
price: number
quantity: number
}
/**
* 购物车管理器
* 使用粘性事件实现多页面同步
*/
export class CartManager {
private static instance: CartManager
private cartState: CartState = {
items: [],
totalCount: 0,
totalPrice: 0
}
private constructor() {}
static getInstance(): CartManager {
if (!CartManager.instance) {
CartManager.instance = new CartManager()
}
return CartManager.instance
}
/**
* 添加商品到购物车
*/
async addItem(item: CartItem): Promise<void> {
// 查找是否已存在
const existingIndex = this.cartState.items.findIndex(i => i.id === item.id)
if (existingIndex >= 0) {
// 增加数量
this.cartState.items[existingIndex].quantity += item.quantity
} else {
// 添加新商品
this.cartState.items.push(item)
}
// 更新统计
this.updateStats()
// 发布粘性事件
await this.publishCartState()
}
/**
* 移除商品
*/
async removeItem(itemId: string): Promise<void> {
this.cartState.items = this.cartState.items.filter(i => i.id !== itemId)
this.updateStats()
await this.publishCartState()
}
/**
* 更新商品数量
*/
async updateQuantity(itemId: string, quantity: number): Promise<void> {
const item = this.cartState.items.find(i => i.id === itemId)
if (item) {
if (quantity <= 0) {
await this.removeItem(itemId)
} else {
item.quantity = quantity
this.updateStats()
await this.publishCartState()
}
}
}
/**
* 清空购物车
*/
async clearCart(): Promise<void> {
this.cartState = {
items: [],
totalCount: 0,
totalPrice: 0
}
await this.publishCartState()
}
/**
* 更新统计信息
*/
private updateStats(): void {
this.cartState.totalCount = this.cartState.items.reduce((sum, item) => sum + item.quantity, 0)
this.cartState.totalPrice = this.cartState.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
}
/**
* 发布购物车状态(粘性事件)
*/
private async publishCartState(): Promise<void> {
const options = {
event: 'com.example.CART_STATE',
data: JSON.stringify(this.cartState),
isSticky: true,
parameters: {
totalCount: this.cartState.totalCount,
totalPrice: this.cartState.totalPrice
}
}
await commonEventManager.publish(options)
hilog.info(DOMAIN, TAG, `Cart state published: ${this.cartState.totalCount} items`)
}
/**
* 获取当前购物车状态
*/
getCartState(): CartState {
return { ...this.cartState }
}
}
/**
* 商品列表页
*/
@Entry
@Component
struct ProductListPage {
@State cartCount: number = 0
private cartManager: CartManager = CartManager.getInstance()
private subscriber: StickyEventSubscriber = new StickyEventSubscriber()
async aboutToAppear() {
// 订阅购物车状态(粘性事件)
this.subscriber.setOnCartStateChange((state: CartState) => {
this.cartCount = state.totalCount
})
await this.subscriber.startSubscribing()
}
async aboutToDisappear() {
await this.subscriber.stopSubscribing()
}
build() {
Column() {
// 购物车图标(显示数量)
Row() {
Text('商品列表')
.fontSize(20)
.fontWeight(FontWeight.Bold)
Blank()
Badge({ count: this.cartCount, position: BadgePosition.RightTop }) {
Image($r('app.media.cart_icon'))
.width(30)
.height(30)
}
.onClick(() => {
router.pushUrl({ url: 'pages/CartPage' })
})
}
.width('100%')
.padding(10)
// 商品列表
List() {
ForEach(['商品A', '商品B', '商品C'], (name: string) => {
ListItem() {
Row() {
Text(name)
Blank()
Button('加入购物车')
.onClick(async () => {
await this.cartManager.addItem({
id: name,
name: name,
price: 99.9,
quantity: 1
})
})
}
.width('100%')
.padding(10)
}
})
}
.width('100%')
.layoutWeight(1)
}
.width('100%')
.height('100%')
}
}
/**
* 购物车页面
*/
@Entry
@Component
struct CartPage {
@State cartState: CartState = {
items: [],
totalCount: 0,
totalPrice: 0
}
private cartManager: CartManager = CartManager.getInstance()
private subscriber: StickyEventSubscriber = new StickyEventSubscriber()
async aboutToAppear() {
// 订阅购物车状态
// 粘性事件:立即获取当前购物车状态
this.subscriber.setOnCartStateChange((state: CartState) => {
this.cartState = state
})
await this.subscriber.startSubscribing()
hilog.info(DOMAIN, TAG, `Cart page loaded with ${this.cartState.totalCount} items`)
}
async aboutToDisappear() {
await this.subscriber.stopSubscribing()
}
build() {
Column() {
Text('购物车')
.fontSize(24)
.fontWeight(FontWeight.Bold)
List() {
ForEach(this.cartState.items, (item: CartItem) => {
ListItem() {
Row() {
Text(item.name)
Blank()
Text(`¥${item.price}`)
Text(` x ${item.quantity}`)
Button('删除')
.onClick(async () => {
await this.cartManager.removeItem(item.id)
})
}
.width('100%')
.padding(10)
}
})
}
.width('100%')
.layoutWeight(1)
// 总价
Row() {
Text(`总计: ¥${this.cartState.totalPrice.toFixed(2)}`)
.fontSize(18)
.fontWeight(FontWeight.Bold)
Blank()
Button('结算')
.enabled(this.cartState.totalCount > 0)
}
.width('100%')
.padding(10)
}
.width('100%')
.height('100%')
}
}
四、踩坑与注意事项
4.1 坑一:粘性事件堆积
问题:多次发布粘性事件,只有最新的会被缓存,旧事件会丢失。
// ❌ 多次发布不同数据
await publishSticky('MY_EVENT', { version: 1 })
await publishSticky('MY_EVENT', { version: 2 })
await publishSticky('MY_EVENT', { version: 3 })
// 新订阅者只会收到 version: 3
// version: 1 和 version: 2 丢失了
// ✅ 如果需要保留历史,使用数组
const history: Data[] = []
history.push({ version: 1 })
history.push({ version: 2 })
history.push({ version: 3 })
await publishSticky('MY_EVENT_HISTORY', history)
4.2 坑二:粘性事件数据过期
问题:缓存的粘性事件可能已经过期。
// ❌ 直接使用粘性事件数据
this.subscriber.setOnDataChange((data) => {
// data可能是很久以前的数据
this.processData(data)
})
// ✅ 检查数据时效性
this.subscriber.setOnDataChange((data) => {
const timestamp = data.parameters?.timestamp ?? 0
const now = Date.now()
const age = now - timestamp
if (age > 60000) { // 数据超过1分钟
// 数据可能过期,重新获取
this.refreshData()
} else {
// 数据新鲜,直接使用
this.processData(data)
}
})
4.3 坑三:订阅时立即回调导致的问题
问题:粘性事件订阅时会立即回调,此时组件可能未完全初始化。
// ❌ 回调时组件未初始化
@Entry
@Component
struct MyPage {
@State data: string = ''
async aboutToAppear() {
// 订阅时立即回调,但this.data可能还未初始化
this.subscriber.setOnDataChange((data) => {
this.data = data // 可能出错
})
await this.subscriber.startSubscribing()
}
}
// ✅ 使用标志位控制
@Entry
@Component
struct MyPage {
@State data: string = ''
private isReady: boolean = false
async aboutToAppear() {
this.subscriber.setOnDataChange((data) => {
if (this.isReady) {
this.data = data
}
})
await this.subscriber.startSubscribing()
// 标记为就绪
this.isReady = true
}
}
4.4 坑四:忘记设置getStickEvent
问题:订阅时忘记设置getStickEvent: true,收不到缓存事件。
// ❌ 忘记设置getStickEvent
const subscribeInfo: CommonEventSubscribeInfo = {
events: ['com.example.MY_EVENT']
// 缺少 getStickEvent: true
}
// 订阅后不会收到缓存的粘性事件
// ✅ 正确设置
const subscribeInfo: CommonEventSubscribeInfo = {
events: ['com.example.MY_EVENT'],
getStickEvent: true // 关键!
}
五、HarmonyOS 6适配指南
5.1 API变更
| 变更项 | HarmonyOS 5 | HarmonyOS 6 |
|---|---|---|
| 粘性事件发布 | isSticky: true |
同上 |
| 粘性事件订阅 | getStickEvent: true |
同上 |
| 清除粘性事件 | 无 | 新增removeStickyEvent() |
5.2 新增功能
// HarmonyOS 6新增:清除粘性事件
import { commonEventManager } from '@kit.BasicServicesKit'
// 清除特定粘性事件
await commonEventManager.removeStickyEvent('com.example.MY_EVENT')
// 清除所有粘性事件
await commonEventManager.removeAllStickyEvents()
// HarmonyOS 6新增:获取粘性事件(不订阅)
const stickyData = await commonEventManager.getStickyEvent('com.example.MY_EVENT')
if (stickyData) {
console.log(`Current sticky event: ${stickyData.data}`)
}
5.3 性能优化
// HarmonyOS 6:粘性事件缓存管理
class StickyEventManager {
// 设置缓存过期时间
static setCacheExpiry(event: string, ttl: number) {
// ttl: 毫秒
// 超过ttl的缓存事件会被清除
}
// 设置缓存大小限制
static setMaxCacheSize(maxSize: number) {
// 限制粘性事件缓存的总大小
}
}
// 使用示例
StickyEventManager.setCacheExpiry('com.example.LOCATION', 300000) // 5分钟
StickyEventManager.setMaxCacheSize(1024 * 1024) // 1MB
六、总结
6.1 核心要点回顾

6.2 最佳实践清单
- [ ] 发布时设置
isSticky: true - [ ] 订阅时设置
getStickEvent: true - [ ] 检查数据时效性
- [ ] 处理组件未初始化的情况
- [ ] 使用时间戳标记数据新鲜度
- [ ] 定期清理过期的粘性事件
- [ ] 避免存储大量数据在粘性事件中
6.3 适用场景总结
| 场景 | 粘性事件优势 |
|---|---|
| 状态同步 | 新页面立即获取当前状态 |
| 配置管理 | 配置变更立即生效 |
| 购物车 | 多页面数据实时同步 |
| 用户登录 | 新页面立即知道登录状态 |
| 网络状态 | 启动时立即获取网络状态 |
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)