@ohos-cpf/3rdloop 0.0.12 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/vendor/Server/Routes/controllers/LoopEngineController.js +36 -3
- package/vendor/Server/Routes/controllers/OrchestratorController.js +193 -9
- package/vendor/Server/Skills/flutter-build-test/SKILL.md +252 -0
- package/vendor/Server/Skills/flutter-build-test/assets/BUILDENV_TEMPLATE.md +42 -0
- package/vendor/Server/Skills/flutter-build-test/assets/BUILD_REPORT_TEMPLATE.md +64 -0
- package/vendor/Server/Skills/flutter-build-test/assets/README_SECTION_TEMPLATE.md +78 -0
- package/vendor/Server/Skills/flutter-build-test/references/BUILD_TROUBLESHOOTING.md +128 -0
- package/vendor/Server/Skills/flutter-build-test/references/DOC_UPDATE_GUIDE.md +119 -0
- package/vendor/Server/Skills/flutter-build-test/references/FLVM_GUIDE.md +89 -0
- package/vendor/Server/Skills/flutter-build-test/scripts/build-matrix.cjs +374 -0
- package/vendor/Server/Skills/flutter-build-test/scripts/locate-example.cjs +189 -0
- package/vendor/Server/Skills/flutter-build-test/scripts/update-buildenv.cjs +171 -0
- package/vendor/Server/Skills/flutter-code-use/SKILL.md +316 -0
- package/vendor/Server/Skills/flutter-code-use/references/event-channel.md +440 -0
- package/vendor/Server/Skills/flutter-code-use/references/federated.md +295 -0
- package/vendor/Server/Skills/flutter-code-use/references/ffi-binding-translate.md +130 -0
- package/vendor/Server/Skills/flutter-code-use/references/ffi-compile-from-source.md +169 -0
- package/vendor/Server/Skills/flutter-code-use/references/ffi-fetch-at-build.md +161 -0
- package/vendor/Server/Skills/flutter-code-use/references/ffi-prebuilt-bundle.md +175 -0
- package/vendor/Server/Skills/flutter-code-use/references/ffi-rhttp-guide.md +235 -0
- package/vendor/Server/Skills/flutter-code-use/references/ffi-rust-cross-compile.md +514 -0
- package/vendor/Server/Skills/flutter-code-use/references/ffi.md +220 -0
- package/vendor/Server/Skills/flutter-code-use/references/method-channel.md +643 -0
- package/vendor/Server/Skills/flutter-code-use/references/monorepo.md +188 -0
- package/vendor/Server/Skills/flutter-code-use/references/ohos-api-pitfalls.md +717 -0
- package/vendor/Server/Skills/flutter-code-use/references/platform-view.md +448 -0
- package/vendor/Server/Skills/flutter-code-use/references/pure-dart.md +180 -0
- package/vendor/Server/Skills/flutter-code-use/references/texture.md +459 -0
- package/vendor/Server/Skills/flutter-demo-code-generator/SKILL.md +270 -0
- package/vendor/Server/Skills/flutter-demo-code-generator/assets/PAGE_TEMPLATES.md +544 -0
- package/vendor/Server/Skills/flutter-demo-code-generator/references/CODE_STANDARDS.md +328 -0
- package/vendor/Server/Skills/flutter-demo-code-generator/references/DEMO_DOC_PARSING.md +126 -0
- package/vendor/Server/Skills/flutter-demo-code-generator/references/EXAMPLES.md +629 -0
- package/vendor/Server/Skills/flutter-demo-code-generator/scripts/validate-flutter-demo.cjs +268 -0
- package/vendor/Server/Skills/flutter-demo-doc-generator/SKILL.md +227 -0
- package/vendor/Server/Skills/flutter-demo-doc-generator/assets/DEMO_DOC_TEMPLATE.md +78 -0
- package/vendor/Server/Skills/flutter-demo-doc-generator/references/COVERAGE_REPORT_PARSING.md +174 -0
- package/vendor/Server/Skills/flutter-demo-doc-generator/references/EXAMPLES.md +162 -0
- package/vendor/Server/Skills/flutter-demo-doc-generator/references/MCP_TOOL_GUIDE.md +123 -0
- package/vendor/Server/Skills/flutter-demo-doc-generator/references/OUTPUT_FORMAT.md +190 -0
- package/vendor/Server/Skills/flutter-demo-doc-generator/references/QUALITY_CHECKLIST.md +83 -0
- package/vendor/Server/Skills/flutter-demo-doc-generator/scripts/validate-skill.cjs +259 -0
- package/vendor/Server/Skills/flutter-library-demo-coverage/SKILL.md +175 -0
- package/vendor/VERSION +3 -3
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
# EventChannel 插件鸿蒙适配
|
|
2
|
+
|
|
3
|
+
## 适用条件
|
|
4
|
+
|
|
5
|
+
- 插件使用 `EventChannel` 进行原生到 Dart 的事件流通信
|
|
6
|
+
- 典型场景:传感器数据、位置更新、网络状态变化、蓝牙扫描结果
|
|
7
|
+
- 大多数 EventChannel 插件同时使用 MethodChannel(混合模式)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 第一部分:工程搭建
|
|
12
|
+
|
|
13
|
+
### 工程创建
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
flutter create -t plugin --platforms ohos .
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### 目录结构
|
|
20
|
+
|
|
21
|
+
与 MethodChannel 插件相同:
|
|
22
|
+
```
|
|
23
|
+
ohos/
|
|
24
|
+
├── src/main/
|
|
25
|
+
│ ├── ets/
|
|
26
|
+
│ │ └── components/
|
|
27
|
+
│ │ └── plugin/
|
|
28
|
+
│ │ └── XxxPlugin.ets
|
|
29
|
+
│ └── resources/
|
|
30
|
+
├── oh-package.json5
|
|
31
|
+
├── build-profile.json5
|
|
32
|
+
└── hvigorfile.ts
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 配置要点
|
|
36
|
+
|
|
37
|
+
> `ohos/` 目录由 `flutter create -t plugin --platforms ohos .` 自动生成。本部分只描述对生成结果的自定义配置。**不要手动创建 `build-profile.json5`、`hvigorfile.ts`、`module.json5` 等配置文件。**
|
|
38
|
+
|
|
39
|
+
**oh-package.json5 — 添加三方依赖(按需)**
|
|
40
|
+
|
|
41
|
+
如果插件需要额外的 ohpm 三方包(来自 `02-planning.json` 的 `native_dependency_mapping`),在 `flutter create` 生成的 `ohos/oh-package.json5` 的 `dependencies` 中追加。
|
|
42
|
+
|
|
43
|
+
> **`@ohos/flutter_ohos` 依赖由 Flutter 构建工具自动注入,无需手动添加。** 如果 `flutter create` 生成了 `"@ohos/flutter_ohos": "file:./libs/flutter.har"`,**必须将其移除**(将 dependencies 设为 `{}`),否则会导致 `Failed to resolve OhmUrl` 编译错误。
|
|
44
|
+
|
|
45
|
+
**module.json5 — 添加权限声明(按需)**
|
|
46
|
+
|
|
47
|
+
如果插件需要系统权限(如传感器、定位等),在 `ohos/src/main/module.json5` 的 `module` 下追加 `requestPermissions`。
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 第二部分:编码实现
|
|
52
|
+
|
|
53
|
+
### 纯 EventChannel 结构
|
|
54
|
+
|
|
55
|
+
```ets
|
|
56
|
+
import {
|
|
57
|
+
FlutterPlugin,
|
|
58
|
+
FlutterPluginBinding
|
|
59
|
+
} from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin';
|
|
60
|
+
import EventChannel, {
|
|
61
|
+
EventSink,
|
|
62
|
+
StreamHandler
|
|
63
|
+
} from '@ohos/flutter_ohos/src/main/ets/plugin/common/EventChannel';
|
|
64
|
+
|
|
65
|
+
export default class XxxPlugin implements FlutterPlugin, StreamHandler {
|
|
66
|
+
private eventChannel: EventChannel | null = null;
|
|
67
|
+
private eventSink: EventSink | null = null;
|
|
68
|
+
|
|
69
|
+
onAttachedToEngine(binding: FlutterPluginBinding): void {
|
|
70
|
+
this.eventChannel = new EventChannel(binding.getBinaryMessenger(), "event_channel_name");
|
|
71
|
+
this.eventChannel.setStreamHandler(this);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
onDetachedFromEngine(binding: FlutterPluginBinding): void {
|
|
75
|
+
this.eventChannel?.setStreamHandler(null);
|
|
76
|
+
this.eventChannel = null;
|
|
77
|
+
this.eventSink = null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
onListen(args: Object, events: EventSink): void {
|
|
81
|
+
this.eventSink = events;
|
|
82
|
+
this.startListening(args);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
onCancel(args: Object): void {
|
|
86
|
+
this.stopListening();
|
|
87
|
+
this.eventSink = null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private startListening(args: Object): void {
|
|
91
|
+
// 启动原生数据监听
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private stopListening(): void {
|
|
95
|
+
// 停止原生数据监听,释放资源
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### 混合模式(MethodChannel + EventChannel)
|
|
101
|
+
|
|
102
|
+
大多数插件同时使用两种 Channel(MethodChannel 做控制、EventChannel 做数据流):
|
|
103
|
+
|
|
104
|
+
```ets
|
|
105
|
+
import {
|
|
106
|
+
FlutterPlugin,
|
|
107
|
+
FlutterPluginBinding
|
|
108
|
+
} from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin';
|
|
109
|
+
import MethodChannel, {
|
|
110
|
+
MethodCallHandler,
|
|
111
|
+
MethodResult
|
|
112
|
+
} from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
|
|
113
|
+
import MethodCall from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodCall';
|
|
114
|
+
import EventChannel, {
|
|
115
|
+
EventSink,
|
|
116
|
+
StreamHandler
|
|
117
|
+
} from '@ohos/flutter_ohos/src/main/ets/plugin/common/EventChannel';
|
|
118
|
+
|
|
119
|
+
export default class XxxPlugin implements FlutterPlugin, MethodCallHandler, StreamHandler {
|
|
120
|
+
private methodChannel: MethodChannel | null = null;
|
|
121
|
+
private eventChannel: EventChannel | null = null;
|
|
122
|
+
private eventSink: EventSink | null = null;
|
|
123
|
+
private context: common.Context | null = null;
|
|
124
|
+
|
|
125
|
+
onAttachedToEngine(binding: FlutterPluginBinding): void {
|
|
126
|
+
this.context = binding.getApplicationContext();
|
|
127
|
+
|
|
128
|
+
this.methodChannel = new MethodChannel(binding.getBinaryMessenger(), "method_channel_name");
|
|
129
|
+
this.methodChannel.setMethodCallHandler(this);
|
|
130
|
+
|
|
131
|
+
this.eventChannel = new EventChannel(binding.getBinaryMessenger(), "event_channel_name");
|
|
132
|
+
this.eventChannel.setStreamHandler(this);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
onDetachedFromEngine(binding: FlutterPluginBinding): void {
|
|
136
|
+
this.stopListening();
|
|
137
|
+
this.methodChannel?.setMethodCallHandler(null);
|
|
138
|
+
this.methodChannel = null;
|
|
139
|
+
this.eventChannel?.setStreamHandler(null);
|
|
140
|
+
this.eventChannel = null;
|
|
141
|
+
this.eventSink = null;
|
|
142
|
+
this.context = null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
onMethodCall(call: MethodCall, result: MethodResult): void {
|
|
146
|
+
switch (call.method) {
|
|
147
|
+
case "start":
|
|
148
|
+
// 控制命令
|
|
149
|
+
result.success(null);
|
|
150
|
+
break;
|
|
151
|
+
case "stop":
|
|
152
|
+
this.stopListening();
|
|
153
|
+
result.success(null);
|
|
154
|
+
break;
|
|
155
|
+
default:
|
|
156
|
+
result.notImplemented();
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
onListen(args: Object, events: EventSink): void {
|
|
162
|
+
this.eventSink = events;
|
|
163
|
+
this.startListening(args);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
onCancel(args: Object): void {
|
|
167
|
+
this.stopListening();
|
|
168
|
+
this.eventSink = null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private startListening(args: Object): void {
|
|
172
|
+
// 启动原生数据监听
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
private stopListening(): void {
|
|
176
|
+
// 停止原生数据监听
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### 多 EventChannel 场景
|
|
182
|
+
|
|
183
|
+
部分插件有多个独立的事件流(如蓝牙插件:扫描结果流 + 连接状态流),需要用多个 StreamHandler 实现:
|
|
184
|
+
|
|
185
|
+
```ets
|
|
186
|
+
export default class XxxPlugin implements FlutterPlugin, MethodCallHandler {
|
|
187
|
+
private scanChannel: EventChannel | null = null;
|
|
188
|
+
private stateChannel: EventChannel | null = null;
|
|
189
|
+
private scanSink: EventSink | null = null;
|
|
190
|
+
private stateSink: EventSink | null = null;
|
|
191
|
+
|
|
192
|
+
onAttachedToEngine(binding: FlutterPluginBinding): void {
|
|
193
|
+
// 扫描结果事件流
|
|
194
|
+
this.scanChannel = new EventChannel(binding.getBinaryMessenger(), "xxx/scan");
|
|
195
|
+
this.scanChannel.setStreamHandler({
|
|
196
|
+
onListen: (args: Object, events: EventSink): void => {
|
|
197
|
+
this.scanSink = events;
|
|
198
|
+
this.startScan();
|
|
199
|
+
},
|
|
200
|
+
onCancel: (args: Object): void => {
|
|
201
|
+
this.stopScan();
|
|
202
|
+
this.scanSink = null;
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// 连接状态事件流
|
|
207
|
+
this.stateChannel = new EventChannel(binding.getBinaryMessenger(), "xxx/state");
|
|
208
|
+
this.stateChannel.setStreamHandler({
|
|
209
|
+
onListen: (args: Object, events: EventSink): void => {
|
|
210
|
+
this.stateSink = events;
|
|
211
|
+
},
|
|
212
|
+
onCancel: (args: Object): void => {
|
|
213
|
+
this.stateSink = null;
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### 事件发送
|
|
221
|
+
|
|
222
|
+
```ets
|
|
223
|
+
// 发送成功事件(数据可以是任意可序列化类型)
|
|
224
|
+
this.eventSink?.success(data);
|
|
225
|
+
|
|
226
|
+
// 发送 Map 数据
|
|
227
|
+
const eventData = new Map<string, Object>();
|
|
228
|
+
eventData.set("type", "update");
|
|
229
|
+
eventData.set("value", 42);
|
|
230
|
+
this.eventSink?.success(eventData);
|
|
231
|
+
|
|
232
|
+
// 发送错误事件
|
|
233
|
+
this.eventSink?.error("ERROR_CODE", "error message", null);
|
|
234
|
+
|
|
235
|
+
// 结束事件流(通常不需要主动调用)
|
|
236
|
+
this.eventSink?.endOfStream();
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Dart 侧接收数据的类型安全(重要)
|
|
240
|
+
|
|
241
|
+
> **`StandardMessageCodec` 的 number 编码陷阱**:ETS 端通过 `eventSink.success()` 发送的 `number` 值,经 `StandardMessageCodec` 编码时会区分整数和浮点数:
|
|
242
|
+
> - 整数值(如 `3.0`、`42`、`180`)→ 编码为 INT32/INT64 → Dart 侧解码为 `int`
|
|
243
|
+
> - 浮点值(如 `3.14`、`180.5`)→ 编码为 FLOAT64 → Dart 侧解码为 `double`
|
|
244
|
+
>
|
|
245
|
+
> 在 Dart null-safe 中,`int` **不能**隐式转为 `double`。如果 Dart 侧将 `dynamic` 直接传入 `double` 类型的构造函数参数,当值恰好为整数时会抛出 `TypeError`,且该异常常被 `onError` 吞掉,导致 stream 静默终止。
|
|
246
|
+
|
|
247
|
+
**规则**:Dart 侧从 EventChannel 接收数据后,凡目标类型为 `double` 的字段,**必须**先转 `num` 再转 `double`;目标类型为 `int` 的字段同理。
|
|
248
|
+
|
|
249
|
+
```dart
|
|
250
|
+
// ❌ 错误:dynamic 直接传入 double 参数,整数值会崩溃
|
|
251
|
+
stream = eventChannel.receiveBroadcastStream().map((data) {
|
|
252
|
+
final list = data as List<dynamic>;
|
|
253
|
+
return SensorEvent(
|
|
254
|
+
x: list[0], // dynamic → 可能是 int!
|
|
255
|
+
y: list[1],
|
|
256
|
+
accuracy: list[2], // 3.0 经编码后变成 int 3
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
// ✅ 正确:(as num).toDouble() 确保类型安全
|
|
261
|
+
stream = eventChannel.receiveBroadcastStream().map((data) {
|
|
262
|
+
final list = data as List<dynamic>;
|
|
263
|
+
return SensorEvent(
|
|
264
|
+
x: (list[0] as num).toDouble(),
|
|
265
|
+
y: (list[1] as num).toDouble(),
|
|
266
|
+
accuracy: (list[2] as num).toDouble(),
|
|
267
|
+
);
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
同理,Map 形式的事件数据也需要转换:
|
|
272
|
+
|
|
273
|
+
```dart
|
|
274
|
+
// ✅ Map 形式事件数据
|
|
275
|
+
stream = eventChannel.receiveBroadcastStream().map((data) {
|
|
276
|
+
final map = data as Map;
|
|
277
|
+
return LocationEvent(
|
|
278
|
+
latitude: (map['lat'] as num).toDouble(),
|
|
279
|
+
longitude: (map['lng'] as num).toDouble(),
|
|
280
|
+
altitude: (map['alt'] as num).toDouble(),
|
|
281
|
+
);
|
|
282
|
+
});
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### 状态流快照与恢复
|
|
286
|
+
|
|
287
|
+
当 EventChannel 表达的是系统/设备当前状态,而该状态可在应用外部改变时,`onListen` 不能只监听未来事件。应先调用 `refreshCurrentState('onListen')` 查询当前真实状态并发送一次;应用回到前台、窗口重新获焦、Flutter restart / engine reattach、Ability attach / reattach 等恢复点也应调用同一个刷新入口。
|
|
288
|
+
|
|
289
|
+
```ets
|
|
290
|
+
onListen(args: Object, events: EventSink): void {
|
|
291
|
+
this.eventSink = events;
|
|
292
|
+
this.refreshCurrentState('onListen');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
private refreshCurrentState(reason: string): void {
|
|
296
|
+
const state = systemApi.getState();
|
|
297
|
+
this.eventSink?.success(this.convertState(state));
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### 资源生命周期管理
|
|
302
|
+
|
|
303
|
+
EventChannel 插件最容易出问题的地方是资源泄漏。必须确保:
|
|
304
|
+
|
|
305
|
+
**Timer 清理**:
|
|
306
|
+
```ets
|
|
307
|
+
private intervalId: number | null = null;
|
|
308
|
+
|
|
309
|
+
private startListening(args: Object): void {
|
|
310
|
+
this.intervalId = setInterval(() => {
|
|
311
|
+
this.eventSink?.success(this.readSensorData());
|
|
312
|
+
}, 100);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private stopListening(): void {
|
|
316
|
+
if (this.intervalId !== null) {
|
|
317
|
+
clearInterval(this.intervalId);
|
|
318
|
+
this.intervalId = null;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**系统回调注销**:
|
|
324
|
+
```ets
|
|
325
|
+
import sensor from '@ohos.sensor';
|
|
326
|
+
|
|
327
|
+
private startListening(args: Object): void {
|
|
328
|
+
sensor.on(sensor.SensorId.ACCELEROMETER, (data: sensor.AccelerometerResponse) => {
|
|
329
|
+
const eventData = new Map<string, Object>();
|
|
330
|
+
eventData.set("x", data.x);
|
|
331
|
+
eventData.set("y", data.y);
|
|
332
|
+
eventData.set("z", data.z);
|
|
333
|
+
this.eventSink?.success(eventData);
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
private stopListening(): void {
|
|
338
|
+
sensor.off(sensor.SensorId.ACCELEROMETER);
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
**资源释放优先级**:
|
|
343
|
+
1. `onCancel` — Dart 端取消订阅时:停止监听、清理 timer/callback
|
|
344
|
+
2. `onDetachedFromEngine` — 插件卸载时:清理一切资源(Channel、Sink、Context、原生监听)
|
|
345
|
+
|
|
346
|
+
**防御性编程**:
|
|
347
|
+
```ets
|
|
348
|
+
// 发送事件前检查 eventSink 是否有效
|
|
349
|
+
private sendEvent(data: Object): void {
|
|
350
|
+
if (this.eventSink !== null) {
|
|
351
|
+
this.eventSink.success(data);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## 第三部分:常见编译错误与修复
|
|
359
|
+
|
|
360
|
+
### 1. `Class 'XxxPlugin' incorrectly implements interface 'StreamHandler'`
|
|
361
|
+
|
|
362
|
+
**原因**:`onListen` 和 `onCancel` 方法签名不正确。
|
|
363
|
+
|
|
364
|
+
**修复**:确保签名完全匹配:
|
|
365
|
+
```ets
|
|
366
|
+
onListen(args: Object, events: EventSink): void { ... }
|
|
367
|
+
onCancel(args: Object): void { ... }
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### 2. `Property 'success' does not exist on type 'EventSink | null'`
|
|
371
|
+
|
|
372
|
+
**原因**:`eventSink` 声明为可空类型,直接调用方法不安全。
|
|
373
|
+
|
|
374
|
+
**修复**:使用可选链 `?.` 或先做非空判断:
|
|
375
|
+
```ets
|
|
376
|
+
this.eventSink?.success(data);
|
|
377
|
+
// 或
|
|
378
|
+
if (this.eventSink !== null) {
|
|
379
|
+
this.eventSink.success(data);
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### 3. EventChannel 和 MethodChannel 同时实现时 import 冲突
|
|
384
|
+
|
|
385
|
+
**原因**:同时从不同路径导入 `MethodCallHandler` 和 `StreamHandler`。
|
|
386
|
+
|
|
387
|
+
**修复**:确保 import 路径正确且不冲突:
|
|
388
|
+
```ets
|
|
389
|
+
import MethodChannel, {
|
|
390
|
+
MethodCallHandler,
|
|
391
|
+
MethodResult
|
|
392
|
+
} from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
|
|
393
|
+
import MethodCall from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodCall';
|
|
394
|
+
import EventChannel, {
|
|
395
|
+
EventSink,
|
|
396
|
+
StreamHandler
|
|
397
|
+
} from '@ohos/flutter_ohos/src/main/ets/plugin/common/EventChannel';
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
### 4. `Cannot find module '@ohos.sensor'` 等系统 API 导入错误
|
|
401
|
+
|
|
402
|
+
**原因**:传感器等 API 可能需要 Kit 级别导入。
|
|
403
|
+
|
|
404
|
+
**修复**:
|
|
405
|
+
- 旧写法:`import sensor from '@ohos.sensor'`
|
|
406
|
+
- 新写法(Kit):`import { sensor } from '@kit.SensorServiceKit'`
|
|
407
|
+
- 通过 `sub-api-lookup` 确认当前版本推荐的导入方式
|
|
408
|
+
|
|
409
|
+
### 5. 匿名 StreamHandler 中 `this` 指向问题
|
|
410
|
+
|
|
411
|
+
**原因**:使用对象字面量实现 StreamHandler 时,箭头函数中 `this` 可能不指向插件实例。
|
|
412
|
+
|
|
413
|
+
**修复**:
|
|
414
|
+
```ets
|
|
415
|
+
// 正确:箭头函数保持 this 指向
|
|
416
|
+
this.eventChannel.setStreamHandler({
|
|
417
|
+
onListen: (args: Object, events: EventSink): void => {
|
|
418
|
+
this.eventSink = events; // this 指向插件实例
|
|
419
|
+
},
|
|
420
|
+
onCancel: (args: Object): void => {
|
|
421
|
+
this.eventSink = null;
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
### 6. `'async' modifier cannot be used here`
|
|
427
|
+
|
|
428
|
+
**原因**:`onListen` / `onCancel` 是 interface 方法,不能声明为 async。
|
|
429
|
+
|
|
430
|
+
**修复**:异步操作提取到独立方法:
|
|
431
|
+
```ets
|
|
432
|
+
onListen(args: Object, events: EventSink): void {
|
|
433
|
+
this.eventSink = events;
|
|
434
|
+
this.startListeningAsync(); // 不要 await,让它异步执行
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
private async startListeningAsync(): Promise<void> {
|
|
438
|
+
// 异步初始化
|
|
439
|
+
}
|
|
440
|
+
```
|