---
title: "事件与尺寸变化"
description: "Kasumi 事件订阅、WebView 重载恢复与窗口尺寸变化。"
---


> 订阅窗口尺寸变化，并正确处理 WebView 重载后的订阅恢复。

## 模块边界

`event` 只负责宿主向当前 WebView 推送事件，不提供跨窗口广播、持久队列或历史回放。事件名和
载荷以 `protocol/commands.json` 为准。

实现入口：

| 入口 | 责任 |
| --- | --- |
| `frontend/src/bridge/ksm.ts` | `KSM.events.on`、`subscribe`、`unsubscribe` 和重载恢复 |
| `frontend/src/bridge/bus.ts` | 本地监听器和事件派发 |
| `windows/src/bridge/ksm_router.cpp` | Windows 订阅表和 `window.resized` |
| `android/app/src/main/java/dev/kasumi/app/bridge/KSMRouter.kt` | Android 订阅表和事件载荷 |

## 平台支持

| 方法 / 事件 | Windows | Android | 权限 |
| --- | --- | --- | --- |
| `KSM.events.on(event, listener)` | 支持 | 支持 | 无 |
| `KSM.subscribe(event)` / `event.subscribe` | 支持 | 支持 | 无 |
| `KSM.unsubscribe(event)` / `event.unsubscribe` | 支持 | 支持 | 无 |
| `window.resized` | 支持 | 支持 | 无 |
| `webview.reloaded` | 支持 | 支持 | 无 |

## 方法参数与返回

| 方法 | 参数 | 返回 |
| --- | --- | --- |
| `KSM.events.on` | `event: string`、`listener: (payload) => void` | 取消监听的函数 |
| `KSM.subscribe` | `event: string` | `Promise<void>` |
| `KSM.unsubscribe` | `event: string` | `Promise<void>` |
| `event.subscribe` | `{ event }` | `{ subscribed: true }` |
| `event.unsubscribe` | `{ event }` | `{ unsubscribed: true }` |

`window.resized` 载荷为：

```ts
interface KSMWindowResizedPayload {
  width: number;
  height: number;
}
```

尺寸单位是**物理内容区像素**，不是 CSS 像素、逻辑外框尺寸或 DPI 缩放后的前端坐标。

## 订阅语义

`KSM.events.on` 在第一个监听器注册时自动发送 `event.subscribe`，最后一个监听器移除时自动
退订。重复订阅同一事件是幂等的。

`webview.reloaded` 是系统事件，不需要订阅。前端收到后会丢弃旧请求、清空能力缓存、重新查询
能力，并恢复 `window.resized` 等已跟踪订阅。业务代码不需要手工监听该事件来恢复桥接。

## 示例

```ts
import { KSM } from '@kasumi/bridge';

const off = KSM.events.on<{ width: number; height: number }>(
  'window.resized',
  ({ width, height }) => {
    console.info(`content area: ${width} x ${height} physical px`);
  }
);

// 页面卸载时解除监听，最后一个监听器会触发自动退订。
window.addEventListener('pagehide', off, { once: true });
```

## 错误行为

| 情况 | 结果 |
| --- | --- |
| 未初始化桥接 | 普通 `Error` |
| 未注册事件名 | 前端 `TypeError`；宿主侧返回 `INVALID_ARGS` |
| 订阅请求超时 | `name === "TIMEOUT"`，监听器会回滚 |
| WebView 重建 | 旧监听器保留，宿主订阅由前端自动恢复 |
| 当前窗口已销毁 | 事件不再投递，迟到事件被忽略 |

## 平台差异

Windows 在 `WM_SIZE` 后发送 `window.resized`；Android 在 WebView 布局尺寸变化后发送。
两端都使用物理内容区尺寸。`webview.reloaded` 在 Windows WebView2 完成加载后发送，在
Android `onPageFinished` 后发送。

## 排查路径

1. 确认事件名存在于 `protocol/commands.json` 的 `events` 段。
2. 确认 `KSM.events.on` 的监听器已注册，且没有被提前解除。
3. 确认宿主返回 `event.subscribe` 成功响应。
4. 检查尺寸是否来自内容区，而不是窗口外框。
5. 页面重载后检查能力查询和订阅恢复是否完成。
6. 窗口销毁后仍收到事件时，检查宿主是否清理了订阅表。
