HarmonyOS APP事件驱动大揭秘

事件驱动:CommonEvent与EventHandler

从"轮询"到"通知",事件驱动让系统更高效

一、背景与动机:为什么需要事件驱动?

1.1 轮询 vs 事件驱动

假设你要等一个快递,有两种方式:

轮询方式:每隔5分钟给快递员打个电话问"到了吗?"

  • 你很累,快递员也很烦
  • 大部分电话都是无效的

事件驱动方式:告诉快递员"到了给我打电话"

  • 你该干嘛干嘛
  • 快递到了才通知你
typescript 复制代码
// ❌ 轮询方式:浪费资源
while (true) {
    if (isPackageArrived()) {
        handlePackage()
        break
    }
    sleep(5000)  // 每5秒检查一次
}

// ✅ 事件驱动:高效响应
registerPackageListener((package) => {
    handlePackage(package)  // 快递到了才执行
})

1.2 鸿蒙的事件体系

鸿蒙提供了完整的事件驱动机制:

组件 作用 适用场景
CommonEvent 系统级事件总线 跨进程、系统事件
EventHandler 线程级事件队列 线程间通信、延时任务
Emitter 进程内事件发射器 组件间通信

#mermaid-svg-Yhp084Ym2LTFaecf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Yhp084Ym2LTFaecf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Yhp084Ym2LTFaecf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Yhp084Ym2LTFaecf .error-icon{fill:#552222;}#mermaid-svg-Yhp084Ym2LTFaecf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Yhp084Ym2LTFaecf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Yhp084Ym2LTFaecf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Yhp084Ym2LTFaecf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Yhp084Ym2LTFaecf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Yhp084Ym2LTFaecf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Yhp084Ym2LTFaecf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Yhp084Ym2LTFaecf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Yhp084Ym2LTFaecf .marker.cross{stroke:#333333;}#mermaid-svg-Yhp084Ym2LTFaecf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Yhp084Ym2LTFaecf p{margin:0;}#mermaid-svg-Yhp084Ym2LTFaecf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Yhp084Ym2LTFaecf .cluster-label text{fill:#333;}#mermaid-svg-Yhp084Ym2LTFaecf .cluster-label span{color:#333;}#mermaid-svg-Yhp084Ym2LTFaecf .cluster-label span p{background-color:transparent;}#mermaid-svg-Yhp084Ym2LTFaecf .label text,#mermaid-svg-Yhp084Ym2LTFaecf span{fill:#333;color:#333;}#mermaid-svg-Yhp084Ym2LTFaecf .node rect,#mermaid-svg-Yhp084Ym2LTFaecf .node circle,#mermaid-svg-Yhp084Ym2LTFaecf .node ellipse,#mermaid-svg-Yhp084Ym2LTFaecf .node polygon,#mermaid-svg-Yhp084Ym2LTFaecf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Yhp084Ym2LTFaecf .rough-node .label text,#mermaid-svg-Yhp084Ym2LTFaecf .node .label text,#mermaid-svg-Yhp084Ym2LTFaecf .image-shape .label,#mermaid-svg-Yhp084Ym2LTFaecf .icon-shape .label{text-anchor:middle;}#mermaid-svg-Yhp084Ym2LTFaecf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Yhp084Ym2LTFaecf .rough-node .label,#mermaid-svg-Yhp084Ym2LTFaecf .node .label,#mermaid-svg-Yhp084Ym2LTFaecf .image-shape .label,#mermaid-svg-Yhp084Ym2LTFaecf .icon-shape .label{text-align:center;}#mermaid-svg-Yhp084Ym2LTFaecf .node.clickable{cursor:pointer;}#mermaid-svg-Yhp084Ym2LTFaecf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Yhp084Ym2LTFaecf .arrowheadPath{fill:#333333;}#mermaid-svg-Yhp084Ym2LTFaecf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Yhp084Ym2LTFaecf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Yhp084Ym2LTFaecf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Yhp084Ym2LTFaecf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Yhp084Ym2LTFaecf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Yhp084Ym2LTFaecf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Yhp084Ym2LTFaecf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Yhp084Ym2LTFaecf .cluster text{fill:#333;}#mermaid-svg-Yhp084Ym2LTFaecf .cluster span{color:#333;}#mermaid-svg-Yhp084Ym2LTFaecf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Yhp084Ym2LTFaecf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Yhp084Ym2LTFaecf rect.text{fill:none;stroke-width:0;}#mermaid-svg-Yhp084Ym2LTFaecf .icon-shape,#mermaid-svg-Yhp084Ym2LTFaecf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Yhp084Ym2LTFaecf .icon-shape p,#mermaid-svg-Yhp084Ym2LTFaecf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Yhp084Ym2LTFaecf .icon-shape .label rect,#mermaid-svg-Yhp084Ym2LTFaecf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Yhp084Ym2LTFaecf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Yhp084Ym2LTFaecf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Yhp084Ym2LTFaecf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-Yhp084Ym2LTFaecf .primary>*{fill:#e8f5e9!important;stroke:#2e7d32!important;stroke-width:2px!important;}#mermaid-svg-Yhp084Ym2LTFaecf .primary span{fill:#e8f5e9!important;stroke:#2e7d32!important;stroke-width:2px!important;}#mermaid-svg-Yhp084Ym2LTFaecf .info>*{fill:#e3f2fd!important;stroke:#1565c0!important;stroke-width:2px!important;}#mermaid-svg-Yhp084Ym2LTFaecf .info span{fill:#e3f2fd!important;stroke:#1565c0!important;stroke-width:2px!important;}#mermaid-svg-Yhp084Ym2LTFaecf .warning>*{fill:#fff3e0!important;stroke:#e65100!important;stroke-width:2px!important;}#mermaid-svg-Yhp084Ym2LTFaecf .warning span{fill:#fff3e0!important;stroke:#e65100!important;stroke-width:2px!important;} &"线程级&"
&"进程级&"
&"系统级&"
CommonEvent

跨进程事件总线
Emitter

进程内事件
EventHandler

线程事件队列
主线程EventHandler
工作线程EventHandler

1.3 CommonEvent vs EventHandler

很多开发者分不清这两个:

  • CommonEvent:系统级事件总线,支持跨进程发布和订阅

    • 例:电量变化、网络状态变化、屏幕亮灭
    • 特点:全局可见、跨进程、需要权限
  • EventHandler:线程级事件队列,用于线程间通信

    • 例:工作线程通知UI线程更新界面
    • 特点:线程私有、高性能、支持延时
typescript 复制代码
// CommonEvent:订阅系统电量变化
CommonEvent.subscribe({
    events: ['android.intent.action.BATTERY_CHANGED']
}, (err, data) => {
    console.log(`电量: ${data.data}%`)
})

// EventHandler:工作线程通知UI
class MyHandler extends EventHandler {
    processEvent(eventId: number, param: unknown) {
        if (eventId === 1) {
            // 在主线程执行UI更新
            updateUI(param)
        }
    }
}

二、核心原理:事件驱动机制

2.1 CommonEvent架构

#mermaid-svg-TkQLDyb9t1hzAEZn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TkQLDyb9t1hzAEZn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TkQLDyb9t1hzAEZn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TkQLDyb9t1hzAEZn .error-icon{fill:#552222;}#mermaid-svg-TkQLDyb9t1hzAEZn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TkQLDyb9t1hzAEZn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TkQLDyb9t1hzAEZn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TkQLDyb9t1hzAEZn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TkQLDyb9t1hzAEZn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TkQLDyb9t1hzAEZn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TkQLDyb9t1hzAEZn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TkQLDyb9t1hzAEZn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TkQLDyb9t1hzAEZn .marker.cross{stroke:#333333;}#mermaid-svg-TkQLDyb9t1hzAEZn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TkQLDyb9t1hzAEZn p{margin:0;}#mermaid-svg-TkQLDyb9t1hzAEZn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TkQLDyb9t1hzAEZn .cluster-label text{fill:#333;}#mermaid-svg-TkQLDyb9t1hzAEZn .cluster-label span{color:#333;}#mermaid-svg-TkQLDyb9t1hzAEZn .cluster-label span p{background-color:transparent;}#mermaid-svg-TkQLDyb9t1hzAEZn .label text,#mermaid-svg-TkQLDyb9t1hzAEZn span{fill:#333;color:#333;}#mermaid-svg-TkQLDyb9t1hzAEZn .node rect,#mermaid-svg-TkQLDyb9t1hzAEZn .node circle,#mermaid-svg-TkQLDyb9t1hzAEZn .node ellipse,#mermaid-svg-TkQLDyb9t1hzAEZn .node polygon,#mermaid-svg-TkQLDyb9t1hzAEZn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TkQLDyb9t1hzAEZn .rough-node .label text,#mermaid-svg-TkQLDyb9t1hzAEZn .node .label text,#mermaid-svg-TkQLDyb9t1hzAEZn .image-shape .label,#mermaid-svg-TkQLDyb9t1hzAEZn .icon-shape .label{text-anchor:middle;}#mermaid-svg-TkQLDyb9t1hzAEZn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TkQLDyb9t1hzAEZn .rough-node .label,#mermaid-svg-TkQLDyb9t1hzAEZn .node .label,#mermaid-svg-TkQLDyb9t1hzAEZn .image-shape .label,#mermaid-svg-TkQLDyb9t1hzAEZn .icon-shape .label{text-align:center;}#mermaid-svg-TkQLDyb9t1hzAEZn .node.clickable{cursor:pointer;}#mermaid-svg-TkQLDyb9t1hzAEZn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TkQLDyb9t1hzAEZn .arrowheadPath{fill:#333333;}#mermaid-svg-TkQLDyb9t1hzAEZn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TkQLDyb9t1hzAEZn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TkQLDyb9t1hzAEZn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TkQLDyb9t1hzAEZn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TkQLDyb9t1hzAEZn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TkQLDyb9t1hzAEZn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TkQLDyb9t1hzAEZn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TkQLDyb9t1hzAEZn .cluster text{fill:#333;}#mermaid-svg-TkQLDyb9t1hzAEZn .cluster span{color:#333;}#mermaid-svg-TkQLDyb9t1hzAEZn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TkQLDyb9t1hzAEZn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TkQLDyb9t1hzAEZn rect.text{fill:none;stroke-width:0;}#mermaid-svg-TkQLDyb9t1hzAEZn .icon-shape,#mermaid-svg-TkQLDyb9t1hzAEZn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TkQLDyb9t1hzAEZn .icon-shape p,#mermaid-svg-TkQLDyb9t1hzAEZn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TkQLDyb9t1hzAEZn .icon-shape .label rect,#mermaid-svg-TkQLDyb9t1hzAEZn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TkQLDyb9t1hzAEZn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TkQLDyb9t1hzAEZn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TkQLDyb9t1hzAEZn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-TkQLDyb9t1hzAEZn .publisher>*{fill:#e8f5e9!important;stroke:#2e7d32!important;stroke-width:2px!important;}#mermaid-svg-TkQLDyb9t1hzAEZn .publisher span{fill:#e8f5e9!important;stroke:#2e7d32!important;stroke-width:2px!important;}#mermaid-svg-TkQLDyb9t1hzAEZn .manager>*{fill:#fff3e0!important;stroke:#e65100!important;stroke-width:2px!important;}#mermaid-svg-TkQLDyb9t1hzAEZn .manager span{fill:#fff3e0!important;stroke:#e65100!important;stroke-width:2px!important;}#mermaid-svg-TkQLDyb9t1hzAEZn .subscriber>*{fill:#e3f2fd!important;stroke:#1565c0!important;stroke-width:2px!important;}#mermaid-svg-TkQLDyb9t1hzAEZn .subscriber span{fill:#e3f2fd!important;stroke:#1565c0!important;stroke-width:2px!important;} &"进程C(订阅者)&"
&"进程B(订阅者)&"
&"系统服务&"
&"进程A(发布者)&"
Binder IPC
匹配订阅者
匹配订阅者
业务代码
CommonEvent.publish
CommonEventManager

事件管理服务
订阅者列表
粘性事件缓存
CommonEvent.subscribe
回调函数
CommonEvent.subscribe
回调函数

2.2 EventHandler架构

主线程 事件队列 EventHandler 工作线程 主线程 事件队列 EventHandler 工作线程 #mermaid-svg-7dyUGh767ymB2htd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7dyUGh767ymB2htd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7dyUGh767ymB2htd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7dyUGh767ymB2htd .error-icon{fill:#552222;}#mermaid-svg-7dyUGh767ymB2htd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7dyUGh767ymB2htd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7dyUGh767ymB2htd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7dyUGh767ymB2htd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7dyUGh767ymB2htd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7dyUGh767ymB2htd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7dyUGh767ymB2htd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7dyUGh767ymB2htd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7dyUGh767ymB2htd .marker.cross{stroke:#333333;}#mermaid-svg-7dyUGh767ymB2htd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7dyUGh767ymB2htd p{margin:0;}#mermaid-svg-7dyUGh767ymB2htd .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7dyUGh767ymB2htd text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-7dyUGh767ymB2htd .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-7dyUGh767ymB2htd .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-7dyUGh767ymB2htd .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-7dyUGh767ymB2htd .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-7dyUGh767ymB2htd #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-7dyUGh767ymB2htd .sequenceNumber{fill:white;}#mermaid-svg-7dyUGh767ymB2htd #sequencenumber{fill:#333;}#mermaid-svg-7dyUGh767ymB2htd #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-7dyUGh767ymB2htd .messageText{fill:#333;stroke:none;}#mermaid-svg-7dyUGh767ymB2htd .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7dyUGh767ymB2htd .labelText,#mermaid-svg-7dyUGh767ymB2htd .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-7dyUGh767ymB2htd .loopText,#mermaid-svg-7dyUGh767ymB2htd .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-7dyUGh767ymB2htd .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-7dyUGh767ymB2htd .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-7dyUGh767ymB2htd .noteText,#mermaid-svg-7dyUGh767ymB2htd .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-7dyUGh767ymB2htd .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7dyUGh767ymB2htd .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7dyUGh767ymB2htd .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-7dyUGh767ymB2htd .actorPopupMenu{position:absolute;}#mermaid-svg-7dyUGh767ymB2htd .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-7dyUGh767ymB2htd .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-7dyUGh767ymB2htd .actor-man circle,#mermaid-svg-7dyUGh767ymB2htd line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-7dyUGh767ymB2htd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} loop事件循环 postEvent(eventId, param)入队事件按时间排序取出事件返回事件processEvent()执行回调

2.3 事件分发流程

typescript 复制代码
// CommonEvent分发流程(伪代码)
class CommonEventManager {
    private subscribers: Map<string, Subscriber[]> = new Map()
    private stickyEvents: Map<string, CommonEventData> = new Map()
  
    // 发布事件
    async publish(event: string, data: unknown, isSticky: boolean) {
        // 如果是粘性事件,缓存起来
        if (isSticky) {
            this.stickyEvents.set(event, data)
        }
      
        // 查找所有订阅者
        const subs = this.subscribers.get(event) || []
      
        // 通知所有订阅者
        for (const sub of subs) {
            await sub.callback(data)
        }
    }
  
    // 订阅事件
    subscribe(event: string, callback: Function, wantSticky: boolean) {
        // 添加到订阅者列表
        const subs = this.subscribers.get(event) || []
        subs.push({ callback })
        this.subscribers.set(event, subs)
      
        // 如果需要粘性事件,立即回调
        if (wantSticky && this.stickyEvents.has(event)) {
            callback(this.stickyEvents.get(event))
        }
    }
}

三、代码实战:CommonEvent使用

3.1 发布CommonEvent

typescript 复制代码
// CommonEventPublisher.ets
import { commonEventManager, CommonEventPublishData } from '@kit.BasicServicesKit'
import hilog from '@ohos.hilog'

const TAG = 'CommonEventPublisher'
const DOMAIN = 0xFF00

/**
 * CommonEvent发布者示例
 */
export class CommonEventPublisher {
  
    /**
     * 发布简单事件
     */
    static async publishSimpleEvent(): Promise<void> {
        try {
            // 发布简单事件(无参数)
            await commonEventManager.publish('com.example.MY_EVENT')
            hilog.info(DOMAIN, TAG, 'Simple event published')
        } catch (err) {
            hilog.error(DOMAIN, TAG, `Publish failed: ${err.message}`)
        }
    }
  
    /**
     * 发布带数据的事件
     */
    static async publishEventWithData(): Promise<void> {
        // 构建事件数据
        const options: CommonEventPublishData = {
            // 事件名称
            event: 'com.example.DATA_EVENT',
          
            // 事件数据(字符串)
            data: 'Hello from publisher',
          
            // 附加参数(键值对)
            parameters: {
                'userId': 1001,
                'userName': '张三',
                'timestamp': Date.now()
            }
        }
      
        try {
            await commonEventManager.publish(options)
            hilog.info(DOMAIN, TAG, 'Event with data published')
        } catch (err) {
            hilog.error(DOMAIN, TAG, `Publish failed: ${err.message}`)
        }
    }
  
    /**
     * 发布粘性事件
     * 粘性事件会被缓存,新订阅者订阅时立即收到
     */
    static async publishStickyEvent(): Promise<void> {
        const options: CommonEventPublishData = {
            event: 'com.example.STICKY_EVENT',
            data: 'This is a sticky event',
          
            // 标记为粘性事件
            isSticky: true,
          
            parameters: {
                'config': { theme: 'dark', lang: 'zh' }
            }
        }
      
        try {
            await commonEventManager.publish(options)
            hilog.info(DOMAIN, TAG, 'Sticky event published')
        } catch (err) {
            hilog.error(DOMAIN, TAG, `Publish failed: ${err.message}`)
        }
    }
  
    /**
     * 发布有序事件
     * 有序事件会按优先级依次分发给订阅者
     */
    static async publishOrderedEvent(): Promise<void> {
        const options: CommonEventPublishData = {
            event: 'com.example.ORDERED_EVENT',
            data: 'Ordered event data',
          
            // 标记为有序事件
            isOrdered: true
        }
      
        try {
            await commonEventManager.publish(options)
            hilog.info(DOMAIN, TAG, 'Ordered event published')
        } catch (err) {
            hilog.error(DOMAIN, TAG, `Publish failed: ${err.message}`)
        }
    }
  
    /**
     * 发布系统事件
     * 需要系统权限
     */
    static async publishSystemEvent(): Promise<void> {
        // 注意:系统事件需要系统签名或相应权限
        const options: CommonEventPublishData = {
            // 系统预定义事件
            event: 'android.intent.action.BATTERY_LOW',
            parameters: {
                'level': 15
            }
        }
      
        try {
            await commonEventManager.publish(options)
            hilog.info(DOMAIN, TAG, 'System event published')
        } catch (err) {
            hilog.error(DOMAIN, TAG, `Publish system event failed: ${err.message}`)
        }
    }
}

3.2 订阅CommonEvent

typescript 复制代码
// CommonEventSubscriber.ets
import { commonEventManager, CommonEventSubscribeInfo, CommonEventData } from '@kit.BasicServicesKit'
import hilog from '@ohos.hilog'

const TAG = 'CommonEventSubscriber'
const DOMAIN = 0xFF00

/**
 * CommonEvent订阅者示例
 */
export class CommonEventSubscriber {
    private subscriber: commonEventManager.CommonEventSubscriber = null
  
    /**
     * 订阅简单事件
     */
    async subscribeSimpleEvent(): Promise<void> {
        // 构建订阅信息
        const subscribeInfo: CommonEventSubscribeInfo = {
            events: ['com.example.MY_EVENT']
        }
      
        try {
            // 创建订阅者
            this.subscriber = await commonEventManager.createSubscriber(subscribeInfo)
          
            // 注册回调
            this.subscriber.subscribe((err, data: CommonEventData) => {
                if (err) {
                    hilog.error(DOMAIN, TAG, `Subscribe error: ${err.message}`)
                    return
                }
              
                hilog.info(DOMAIN, TAG, `Received event: ${data.event}`)
                hilog.info(DOMAIN, TAG, `Event data: ${data.data}`)
            })
          
            hilog.info(DOMAIN, TAG, 'Subscribed to simple event')
        } catch (err) {
            hilog.error(DOMAIN, TAG, `Subscribe failed: ${err.message}`)
        }
    }
  
    /**
     * 订阅多个事件
     */
    async subscribeMultipleEvents(): Promise<void> {
        const subscribeInfo: CommonEventSubscribeInfo = {
            // 订阅多个事件
            events: [
                'com.example.EVENT_A',
                'com.example.EVENT_B',
                'com.example.EVENT_C'
            ]
        }
      
        try {
            this.subscriber = await commonEventManager.createSubscriber(subscribeInfo)
          
            this.subscriber.subscribe((err, data: CommonEventData) => {
                if (err) {
                    hilog.error(DOMAIN, TAG, `Error: ${err.message}`)
                    return
                }
              
                // 根据事件类型处理
                switch (data.event) {
                    case 'com.example.EVENT_A':
                        this.handleEventA(data)
                        break
                    case 'com.example.EVENT_B':
                        this.handleEventB(data)
                        break
                    case 'com.example.EVENT_C':
                        this.handleEventC(data)
                        break
                }
            })
          
            hilog.info(DOMAIN, TAG, 'Subscribed to multiple events')
        } catch (err) {
            hilog.error(DOMAIN, TAG, `Subscribe failed: ${err.message}`)
        }
    }
  
    private handleEventA(data: CommonEventData) {
        hilog.info(DOMAIN, TAG, `Event A: ${data.data}`)
    }
  
    private handleEventB(data: CommonEventData) {
        hilog.info(DOMAIN, TAG, `Event B: ${data.data}`)
    }
  
    private handleEventC(data: CommonEventData) {
        hilog.info(DOMAIN, TAG, `Event C: ${data.data}`)
    }
  
    /**
     * 订阅粘性事件
     * 订阅时会立即收到之前发布的粘性事件
     */
    async subscribeStickyEvent(): Promise<void> {
        const subscribeInfo: CommonEventSubscribeInfo = {
            events: ['com.example.STICKY_EVENT'],
          
            // 请求接收粘性事件
            getStickEvent: true
        }
      
        try {
            this.subscriber = await commonEventManager.createSubscriber(subscribeInfo)
          
            this.subscriber.subscribe((err, data: CommonEventData) => {
                if (err) {
                    hilog.error(DOMAIN, TAG, `Error: ${err.message}`)
                    return
                }
              
                hilog.info(DOMAIN, TAG, `Received sticky event: ${data.data}`)
              
                // 读取附加参数
                const params = data.parameters
                if (params && params.config) {
                    hilog.info(DOMAIN, TAG, `Config: ${JSON.stringify(params.config)}`)
                }
            })
          
            hilog.info(DOMAIN, TAG, 'Subscribed to sticky event')
        } catch (err) {
            hilog.error(DOMAIN, TAG, `Subscribe failed: ${err.message}`)
        }
    }
  
    /**
     * 取消订阅
     */
    async unsubscribe(): Promise<void> {
        if (this.subscriber) {
            try {
                await this.subscriber.unsubscribe()
                hilog.info(DOMAIN, TAG, 'Unsubscribed')
                this.subscriber = null
            } catch (err) {
                hilog.error(DOMAIN, TAG, `Unsubscribe failed: ${err.message}`)
            }
        }
    }
}

3.3 EventHandler使用

typescript 复制代码
// EventHandlerDemo.ets
import { EventHandler, EventRunner } from '@kit.BasicServicesKit'
import hilog from '@ohos.hilog'

const TAG = 'EventHandlerDemo'
const DOMAIN = 0xFF00

/**
 * 自定义EventHandler
 */
export class MyEventHandler extends EventHandler {
  
    /**
     * 处理事件的核心方法
     * 在关联的线程中执行
     */
    processEvent(eventId: number, param: unknown): void {
        hilog.info(DOMAIN, TAG, `Processing event: ${eventId}`)
      
        switch (eventId) {
            case 1:
                this.handleUpdateUI(param)
                break
            case 2:
                this.handleDataLoaded(param)
                break
            case 3:
                this.handleNetworkResponse(param)
                break
            default:
                hilog.warn(DOMAIN, TAG, `Unknown event: ${eventId}`)
        }
    }
  
    private handleUpdateUI(param: unknown) {
        // 在主线程更新UI
        hilog.info(DOMAIN, TAG, `Update UI: ${JSON.stringify(param)}`)
    }
  
    private handleDataLoaded(param: unknown) {
        hilog.info(DOMAIN, TAG, `Data loaded: ${JSON.stringify(param)}`)
    }
  
    private handleNetworkResponse(param: unknown) {
        hilog.info(DOMAIN, TAG, `Network response: ${JSON.stringify(param)}`)
    }
}

/**
 * EventHandler使用示例
 */
export class EventHandlerExample {
    private mainHandler: MyEventHandler = null
    private workerHandler: MyEventHandler = null
    private workerRunner: EventRunner = null
  
    /**
     * 初始化主线程Handler
     */
    initMainHandler(): void {
        // 获取主线程的EventRunner
        const mainRunner = EventRunner.getMainEventRunner()
      
        // 创建主线程Handler
        this.mainHandler = new MyEventHandler(mainRunner)
      
        hilog.info(DOMAIN, TAG, 'Main handler initialized')
    }
  
    /**
     * 初始化工作线程Handler
     */
    async initWorkerHandler(): Promise<void> {
        // 创建工作线程的EventRunner
        this.workerRunner = EventRunner.create('WorkerThread')
      
        // 启动事件循环
        this.workerRunner.run()
      
        // 创建工作线程Handler
        this.workerHandler = new MyEventHandler(this.workerRunner)
      
        hilog.info(DOMAIN, TAG, 'Worker handler initialized')
    }
  
    /**
     * 发送即时事件
     */
    sendImmediateEvent(): void {
        // 发送到主线程
        this.mainHandler.sendEvent({
            eventId: 1,
            param: { message: 'Update UI now' }
        })
      
        hilog.info(DOMAIN, TAG, 'Immediate event sent')
    }
  
    /**
     * 发送延时事件
     */
    sendDelayedEvent(): void {
        // 延时5秒发送
        this.mainHandler.sendEvent({
            eventId: 2,
            param: { message: 'Delayed data' }
        }, 5000)  // 延时5000毫秒
      
        hilog.info(DOMAIN, TAG, 'Delayed event scheduled (5s)')
    }
  
    /**
     * 发送周期性事件
     */
    sendPeriodicEvent(): void {
        // 每3秒发送一次
        this.mainHandler.sendEvent({
            eventId: 3,
            param: { message: 'Periodic update' }
        }, 3000, true)  // 第三个参数表示周期性
      
        hilog.info(DOMAIN, TAG, 'Periodic event scheduled (every 3s)')
    }
  
    /**
     * 线程间通信示例
     * 工作线程完成任务后通知主线程
     */
    async crossThreadCommunication(): Promise<void> {
        // 在工作线程执行任务
        this.workerHandler.sendEvent({
            eventId: 100,
            param: { task: 'heavyWork' }
        })
      
        // 模拟工作线程处理后通知主线程
        setTimeout(() => {
            // 工作线程发送结果到主线程
            this.mainHandler.sendEvent({
                eventId: 1,
                param: {
                    result: 'Work completed',
                    data: [1, 2, 3, 4, 5]
                }
            })
        }, 2000)
      
        hilog.info(DOMAIN, TAG, 'Cross-thread communication started')
    }
  
    /**
     * 移除事件
     */
    removeEvent(eventId: number): void {
        this.mainHandler.removeEvent(eventId)
        hilog.info(DOMAIN, TAG, `Event ${eventId} removed`)
    }
  
    /**
     * 清理资源
     */
    cleanup(): void {
        if (this.workerRunner) {
            this.workerRunner.stop()
        }
        hilog.info(DOMAIN, TAG, 'Cleanup completed')
    }
}

3.4 实战:网络请求与UI更新

typescript 复制代码
// NetworkWithEventHandler.ets
import { EventHandler, EventRunner } from '@kit.BasicServicesKit'
import http from '@ohos.net.http'
import hilog from '@ohos.hilog'

const TAG = 'NetworkDemo'
const DOMAIN = 0xFF00

// 事件ID定义
const enum EventId {
    NETWORK_SUCCESS = 1,
    NETWORK_ERROR = 2,
    UPDATE_PROGRESS = 3
}

/**
 * 网络请求结果
 */
interface NetworkResult {
    url: string
    data: string
    statusCode: number
}

/**
 * 网络请求管理器
 * 使用EventHandler实现线程安全的网络请求
 */
export class NetworkManager {
    private mainHandler: EventHandler
    private httpRequest: http.HttpRequest
  
    constructor() {
        // 获取主线程Handler
        const mainRunner = EventRunner.getMainEventRunner()
        this.mainHandler = new EventHandler(mainRunner)
      
        // 创建HTTP请求对象
        this.httpRequest = http.createHttp()
    }
  
    /**
     * 发起网络请求
     * 在工作线程执行,完成后通知主线程
     */
    async fetchData(url: string): Promise<void> {
        hilog.info(DOMAIN, TAG, `Fetching: ${url}`)
      
        try {
            // 发起请求
            const response = await this.httpRequest.request(url, {
                method: http.RequestMethod.GET,
                connectTimeout: 60000,
                readTimeout: 60000
            })
          
            // 请求成功,通知主线程
            const result: NetworkResult = {
                url: url,
                data: response.result as string,
                statusCode: response.responseCode
            }
          
            this.mainHandler.sendEvent({
                eventId: EventId.NETWORK_SUCCESS,
                param: result
            })
          
        } catch (err) {
            // 请求失败,通知主线程
            this.mainHandler.sendEvent({
                eventId: EventId.NETWORK_ERROR,
                param: {
                    url: url,
                    error: err.message
                }
            })
        }
    }
  
    /**
     * 带进度的下载
     */
    async downloadWithProgress(url: string, destPath: string): Promise<void> {
        hilog.info(DOMAIN, TAG, `Downloading: ${url}`)
      
        // 模拟进度更新
        for (let progress = 0; progress <= 100; progress += 10) {
            // 通知主线程更新进度
            this.mainHandler.sendEvent({
                eventId: EventId.UPDATE_PROGRESS,
                param: {
                    url: url,
                    progress: progress
                }
            })
          
            // 模拟下载延迟
            await new Promise(resolve => setTimeout(resolve, 500))
        }
      
        // 下载完成
        this.mainHandler.sendEvent({
            eventId: EventId.NETWORK_SUCCESS,
            param: {
                url: url,
                data: destPath,
                statusCode: 200
            }
        })
    }
  
    /**
     * 销毁
     */
    destroy(): void {
        this.httpRequest.destroy()
    }
}

/**
 * UI组件使用示例
 */
@Entry
@Component
struct NetworkPage {
    @State data: string = 'Loading...'
    @State progress: number = 0
    @State errorMessage: string = ''
  
    private networkManager: NetworkManager = new NetworkManager()
    private eventHandler: EventHandler
  
    aboutToAppear() {
        // 创建EventHandler处理网络结果
        const mainRunner = EventRunner.getMainEventRunner()
        this.eventHandler = new EventHandler(mainRunner)
      
        // 处理事件
        this.eventHandler.setEventHandler((eventId, param) => {
            switch (eventId) {
                case EventId.NETWORK_SUCCESS:
                    this.handleSuccess(param as NetworkResult)
                    break
                case EventId.NETWORK_ERROR:
                    this.handleError(param)
                    break
                case EventId.UPDATE_PROGRESS:
                    this.handleProgress(param)
                    break
            }
        })
      
        // 发起请求
        this.networkManager.fetchData('https://api.example.com/data')
    }
  
    aboutToDisappear() {
        this.networkManager.destroy()
    }
  
    private handleSuccess(result: NetworkResult) {
        this.data = result.data
        hilog.info(DOMAIN, TAG, `Data loaded: ${result.data.length} chars`)
    }
  
    private handleError(error: { url: string, error: string }) {
        this.errorMessage = error.error
        hilog.error(DOMAIN, TAG, `Error: ${error.error}`)
    }
  
    private handleProgress(info: { url: string, progress: number }) {
        this.progress = info.progress
    }
  
    build() {
        Column() {
            Text('Network Data')
                .fontSize(24)
                .fontWeight(FontWeight.Bold)
          
            if (this.errorMessage) {
                Text(`Error: ${this.errorMessage}`)
                    .fontColor(Color.Red)
            } else if (this.progress > 0 && this.progress < 100) {
                Progress({ value: this.progress, total: 100, type: ProgressType.Linear })
                    .width('80%')
                Text(`${this.progress}%`)
            } else {
                Text(this.data)
                    .maxLines(10)
                    .textOverflow({ overflow: TextOverflow.Ellipsis })
            }
        }
        .width('100%')
        .height('100%')
        .justifyContent(FlexAlign.Center)
    }
}

四、踩坑与注意事项

4.1 坑一:CommonEvent订阅未取消

问题:订阅后忘记取消,导致内存泄漏和重复回调。

typescript 复制代码
// ❌ 错误示范:只订阅不取消
async subscribeEvent() {
    const subscriber = await commonEventManager.createSubscriber({
        events: ['com.example.EVENT']
    })
  
    subscriber.subscribe((err, data) => {
        // 处理事件
    })
  
    // 没有保存subscriber引用,无法取消!
}

// ✅ 正确做法:保存引用,适时取消
export class MyComponent {
    private subscriber: commonEventManager.CommonEventSubscriber = null
  
    async aboutToAppear() {
        this.subscriber = await commonEventManager.createSubscriber({
            events: ['com.example.EVENT']
        })
      
        this.subscriber.subscribe((err, data) => {
            // 处理事件
        })
    }
  
    async aboutToDisappear() {
        // 组件销毁时取消订阅
        if (this.subscriber) {
            await this.subscriber.unsubscribe()
        }
    }
}

4.2 坑二:EventHandler在错误线程

问题:在非主线程创建的EventHandler无法更新UI。

typescript 复制代码
// ❌ 错误示范:在工作线程创建Handler
import taskpool from '@ohos.taskpool'

@taskpool.task
function backgroundWork() {
    // 这里创建的Handler不在主线程!
    const handler = new EventHandler(EventRunner.create())
  
    handler.sendEvent({
        eventId: 1,
        param: { data: 'result' }
    })
    // 这个事件不会在主线程执行
}

// ✅ 正确做法:使用主线程Handler
export class CorrectExample {
    private mainHandler: EventHandler
  
    constructor() {
        // 在主线程创建Handler
        const mainRunner = EventRunner.getMainEventRunner()
        this.mainHandler = new EventHandler(mainRunner)
    }
  
    async doWork() {
        // 工作线程执行任务
        const result = await taskpool.execute(backgroundTask)
      
        // 通知主线程
        this.mainHandler.sendEvent({
            eventId: 1,
            param: result
        })
    }
}

4.3 坑三:CommonEvent权限问题

问题:某些系统事件需要特殊权限。

typescript 复制代码
// ❌ 订阅系统事件可能失败
await commonEventManager.createSubscriber({
    events: ['android.intent.action.BOOT_COMPLETED']  // 需要系统权限
})

// ✅ 检查权限并处理错误
async subscribeSystemEvent() {
    try {
        const subscriber = await commonEventManager.createSubscriber({
            events: ['android.intent.action.BOOT_COMPLETED']
        })
      
        subscriber.subscribe((err, data) => {
            if (err) {
                if (err.code === 201) {
                    // 权限不足
                    hilog.error(DOMAIN, TAG, 'Permission denied')
                } else {
                    hilog.error(DOMAIN, TAG, `Error: ${err.message}`)
                }
                return
            }
          
            // 处理事件
        })
    } catch (err) {
        hilog.error(DOMAIN, TAG, `Subscribe failed: ${err.message}`)
    }
}

4.4 坑四:事件循环阻塞

问题:EventHandler的事件处理函数执行时间过长,会阻塞后续事件。

typescript 复制代码
// ❌ 错误示范:事件处理函数执行太久
class SlowHandler extends EventHandler {
    processEvent(eventId: number, param: unknown) {
        // 耗时操作阻塞事件循环
        for (let i = 0; i < 1000000000; i++) {
            // 模拟耗时计算
        }
      
        // 后续事件都要等待
    }
}

// ✅ 正确做法:耗时操作异步处理
class FastHandler extends EventHandler {
    processEvent(eventId: number, param: unknown) {
        if (eventId === 1) {
            // 快速提取参数
            const data = param as HeavyData
          
            // 提交到工作线程处理
            taskpool.execute(() => {
                processHeavyData(data)
            })
          
            // 快速返回,不阻塞事件循环
        }
    }
}

4.5 坑五:粘性事件堆积

问题:粘性事件会一直缓存,可能导致数据过期。

typescript 复制代码
// ❌ 粘性事件一直存在
await commonEventManager.publish({
    event: 'com.example.CONFIG',
    data: JSON.stringify({ theme: 'light' }),
    isSticky: true
})

// 后续修改配置,但旧事件还在
await commonEventManager.publish({
    event: 'com.example.CONFIG',
    data: JSON.stringify({ theme: 'dark' }),
    isSticky: true
})
// 现在有两个粘性事件!

// ✅ 方案:发布前先清除旧的粘性事件
async publishConfig(config: object) {
    // 清除旧的粘性事件(如果API支持)
    // 或者在订阅时只处理最新的
    await commonEventManager.publish({
        event: 'com.example.CONFIG',
        data: JSON.stringify(config),
        isSticky: true
    })
}

五、HarmonyOS 6适配指南

5.1 API变更

变更项 HarmonyOS 5 HarmonyOS 6
CommonEvent导入 @ohos.commonEvent @kit.BasicServicesKit
订阅方式 回调嵌套 Promise + 回调分离
EventHandler创建 new EventHandler() new EventHandler(runner)
事件参数 多参数 单对象参数

5.2 新增功能

typescript 复制代码
// HarmonyOS 6新增:事件优先级
const subscribeInfo: CommonEventSubscribeInfo = {
    events: ['com.example.EVENT'],
  
    // 新增:设置订阅优先级(有序事件)
    priority: 100  // 数值越大优先级越高
}

// HarmonyOS 6新增:事件过滤
const subscribeInfo: CommonEventSubscribeInfo = {
    events: ['com.example.EVENT'],
  
    // 新增:设置发布者UID过滤
    publisherDeviceId: 'local',  // 只接收本地设备事件
  
    // 新增:设置发布者PID过滤
    publisherPid: 12345  // 只接收特定进程的事件
}

// HarmonyOS 6新增:异步订阅
async function asyncSubscribe() {
    const subscriber = await commonEventManager.createSubscriber(subscribeInfo)
  
    // 使用Promise风格的订阅
    subscriber.on('receive', (data: CommonEventData) => {
        console.log(`Received: ${data.event}`)
    })
  
    // 错误处理
    subscriber.on('error', (err: Error) => {
        console.error(`Error: ${err.message}`)
    })
}

5.3 性能优化

typescript 复制代码
// HarmonyOS 6:批量发送事件
class BatchEventHandler extends EventHandler {
    // 批量发送,减少上下文切换
    sendBatchEvents(events: Array<{ id: number, param: unknown }>) {
        for (const event of events) {
            this.sendEvent({
                eventId: event.id,
                param: event.param
            })
        }
    }
  
    // 使用延时合并相同事件
    private pendingEvents: Map<number, unknown> = new Map()
  
    sendDebounced(eventId: number, param: unknown, delay: number = 100) {
        // 保存最新参数
        this.pendingEvents.set(eventId, param)
      
        // 延时发送,多次调用会覆盖
        this.sendEvent({ eventId: -1, param: { type: 'debounce', targetId: eventId } }, delay)
    }
  
    processEvent(eventId: number, param: unknown) {
        if (eventId === -1) {
            // 处理延时合并
            const { type, targetId } = param as { type: string, targetId: number }
            if (type === 'debounce') {
                const actualParam = this.pendingEvents.get(targetId)
                this.pendingEvents.delete(targetId)
              
                // 发送实际事件
                this.processEvent(targetId, actualParam)
            }
            return
        }
      
        // 正常事件处理
        // ...
    }
}

六、总结一下下

6.1 核心要点回顾

#mermaid-svg-MJR0LOp0Py2vCp4b{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-MJR0LOp0Py2vCp4b .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-MJR0LOp0Py2vCp4b .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-MJR0LOp0Py2vCp4b .error-icon{fill:#552222;}#mermaid-svg-MJR0LOp0Py2vCp4b .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-MJR0LOp0Py2vCp4b .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-MJR0LOp0Py2vCp4b .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-MJR0LOp0Py2vCp4b .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-MJR0LOp0Py2vCp4b .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-MJR0LOp0Py2vCp4b .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-MJR0LOp0Py2vCp4b .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-MJR0LOp0Py2vCp4b .marker{fill:#333333;stroke:#333333;}#mermaid-svg-MJR0LOp0Py2vCp4b .marker.cross{stroke:#333333;}#mermaid-svg-MJR0LOp0Py2vCp4b svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-MJR0LOp0Py2vCp4b p{margin:0;}#mermaid-svg-MJR0LOp0Py2vCp4b .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-MJR0LOp0Py2vCp4b .cluster-label text{fill:#333;}#mermaid-svg-MJR0LOp0Py2vCp4b .cluster-label span{color:#333;}#mermaid-svg-MJR0LOp0Py2vCp4b .cluster-label span p{background-color:transparent;}#mermaid-svg-MJR0LOp0Py2vCp4b .label text,#mermaid-svg-MJR0LOp0Py2vCp4b span{fill:#333;color:#333;}#mermaid-svg-MJR0LOp0Py2vCp4b .node rect,#mermaid-svg-MJR0LOp0Py2vCp4b .node circle,#mermaid-svg-MJR0LOp0Py2vCp4b .node ellipse,#mermaid-svg-MJR0LOp0Py2vCp4b .node polygon,#mermaid-svg-MJR0LOp0Py2vCp4b .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-MJR0LOp0Py2vCp4b .rough-node .label text,#mermaid-svg-MJR0LOp0Py2vCp4b .node .label text,#mermaid-svg-MJR0LOp0Py2vCp4b .image-shape .label,#mermaid-svg-MJR0LOp0Py2vCp4b .icon-shape .label{text-anchor:middle;}#mermaid-svg-MJR0LOp0Py2vCp4b .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-MJR0LOp0Py2vCp4b .rough-node .label,#mermaid-svg-MJR0LOp0Py2vCp4b .node .label,#mermaid-svg-MJR0LOp0Py2vCp4b .image-shape .label,#mermaid-svg-MJR0LOp0Py2vCp4b .icon-shape .label{text-align:center;}#mermaid-svg-MJR0LOp0Py2vCp4b .node.clickable{cursor:pointer;}#mermaid-svg-MJR0LOp0Py2vCp4b .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-MJR0LOp0Py2vCp4b .arrowheadPath{fill:#333333;}#mermaid-svg-MJR0LOp0Py2vCp4b .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-MJR0LOp0Py2vCp4b .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-MJR0LOp0Py2vCp4b .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MJR0LOp0Py2vCp4b .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-MJR0LOp0Py2vCp4b .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MJR0LOp0Py2vCp4b .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-MJR0LOp0Py2vCp4b .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-MJR0LOp0Py2vCp4b .cluster text{fill:#333;}#mermaid-svg-MJR0LOp0Py2vCp4b .cluster span{color:#333;}#mermaid-svg-MJR0LOp0Py2vCp4b div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-MJR0LOp0Py2vCp4b .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-MJR0LOp0Py2vCp4b rect.text{fill:none;stroke-width:0;}#mermaid-svg-MJR0LOp0Py2vCp4b .icon-shape,#mermaid-svg-MJR0LOp0Py2vCp4b .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-MJR0LOp0Py2vCp4b .icon-shape p,#mermaid-svg-MJR0LOp0Py2vCp4b .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-MJR0LOp0Py2vCp4b .icon-shape .label rect,#mermaid-svg-MJR0LOp0Py2vCp4b .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-MJR0LOp0Py2vCp4b .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-MJR0LOp0Py2vCp4b .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-MJR0LOp0Py2vCp4b :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-MJR0LOp0Py2vCp4b .primary>*{fill:#e8f5e9!important;stroke:#2e7d32!important;stroke-width:2px!important;}#mermaid-svg-MJR0LOp0Py2vCp4b .primary span{fill:#e8f5e9!important;stroke:#2e7d32!important;stroke-width:2px!important;}#mermaid-svg-MJR0LOp0Py2vCp4b .info>*{fill:#e3f2fd!important;stroke:#1565c0!important;stroke-width:2px!important;}#mermaid-svg-MJR0LOp0Py2vCp4b .info span{fill:#e3f2fd!important;stroke:#1565c0!important;stroke-width:2px!important;}#mermaid-svg-MJR0LOp0Py2vCp4b .warning>*{fill:#fff3e0!important;stroke:#e65100!important;stroke-width:2px!important;}#mermaid-svg-MJR0LOp0Py2vCp4b .warning span{fill:#fff3e0!important;stroke:#e65100!important;stroke-width:2px!important;} 事件驱动核心
CommonEvent
EventHandler
跨进程通信
系统事件
粘性事件
线程间通信
延时任务
周期任务

6.2 最佳实践清单

  • CommonEvent订阅后记得取消
  • 使用主线程EventHandler更新UI
  • 处理权限不足的情况
  • 事件处理函数快速返回
  • 耗时操作异步处理
  • 粘性事件注意数据时效性
  • 使用try-catch处理错误
  • 合理设置事件优先级

6.3 选择指南

场景 推荐方案
跨进程通信 CommonEvent
系统事件监听 CommonEvent
线程间通信 EventHandler
UI更新通知 EventHandler(主线程)
延时任务 EventHandler
组件间通信 Emitter