Flutter三方库适配OpenHarmony【doc_text】— onMethodCall 分发与文件路径参数提取
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net。但它的参数提取方式不太一样——用的是call.args而不是,而且整个方法调用是异步的,需要用 Promise 链来处理结果。// extractTextFromDoc 是 async 方法// 内部有 await 操作// 比如 zlib.decompressFile 是异步的// .
前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
doc_text 的 onMethodCall 只处理一个方法:extractTextFromDoc。但它的参数提取方式不太一样——用的是 call.args 而不是 call.argument(),而且整个方法调用是异步的,需要用 Promise 链来处理结果。
一、onMethodCall 完整实现
1.1 源码
onMethodCall(call: MethodCall, result: MethodResult): void {
if (call.method == "extractTextFromDoc") {
const args = call.args as Map<string, Object>;
const filePath = args.get("filePath") as string;
if (!filePath) {
result.error("INVALID_ARGUMENT", "filePath is required", null);
return;
}
this.extractTextFromDoc(filePath).then((text) => {
if (text) {
result.success(text);
} else {
result.error("UNAVAILABLE", "Could not extract text.", null);
}
}).catch((err: Error) => {
result.error("ERROR", err.message, null);
});
} else {
result.notImplemented()
}
}
1.2 方法分发表

二、参数提取方式
2.1 call.args vs call.argument()
// doc_text 的方式:call.args
const args = call.args as Map<string, Object>;
const filePath = args.get("filePath") as string;
// flutter_web_auth 的方式:call.argument()
const url = call.argument('url') as string;
const callbackUrlScheme = call.argument('callbackUrlScheme') as string;
2.2 两种方式的对比
| 维度 | call.args | call.argument() |
|---|---|---|
| 获取方式 | 先转 Map,再 get | 直接按 key 获取 |
| 类型安全 | 需要两次 as 转换 | 需要一次 as 转换 |
| 空值处理 | Map.get 返回 undefined | 返回 ESObject |
| 代码量 | 多一行 | 少一行 |
2.3 Dart 层传递的参数
// Dart 层
methodChannel.invokeMethod<String>(
'extractTextFromDoc',
{'filePath': filePath}, // Map<String, dynamic>
);
// 原生层接收
const args = call.args as Map<string, Object>;
// args = Map { "filePath" → "/data/storage/test.docx" }
2.4 为什么用 Map 而不是直接传字符串
// 方案1:直接传字符串(更简单)
methodChannel.invokeMethod<String>('extractTextFromDoc', filePath);
// 方案2:用 Map 包装(当前实现)
methodChannel.invokeMethod<String>('extractTextFromDoc', {'filePath': filePath});
| 方案 | 优点 | 缺点 |
|---|---|---|
| 直接传字符串 | 简单 | 不可扩展,未来加参数要改接口 |
| Map 包装 | 可扩展,未来加参数不影响 | 多一层解包 |
💡 用 Map 包装是更好的实践。如果未来需要添加参数(比如编码提示、页码范围),只需要在 Map 中加新的 key,不需要改方法签名。
三、空值校验
3.1 代码
if (!filePath) {
result.error("INVALID_ARGUMENT", "filePath is required", null);
return;
}
3.2 !filePath 覆盖的场景
| 场景 | filePath 的值 | !filePath | 处理 |
|---|---|---|---|
| 正常 | "/data/test.docx" |
false | 继续 |
| 空字符串 | "" |
true | 返回错误 |
| null | null |
true | 返回错误 |
| undefined | undefined |
true | 返回错误 |
| 参数缺失 | Map 中没有 “filePath” | true | 返回错误 |
3.3 与 flutter_web_auth 的对比
// flutter_web_auth:没有做空值校验
const url = call.argument('url') as string;
// 如果 url 为 null,openLink(null) 会失败
// doc_text:做了空值校验
if (!filePath) {
result.error("INVALID_ARGUMENT", "filePath is required", null);
return;
}
doc_text 的防御更严格。flutter_web_auth 依赖 Dart 层的 required 关键字来保证参数不为空,doc_text 在原生层也做了一道检查。
四、异步 Promise 链
4.1 代码结构
this.extractTextFromDoc(filePath) // 返回 Promise<string | null>
.then((text) => { // 成功回调
if (text) {
result.success(text);
} else {
result.error("UNAVAILABLE", "Could not extract text.", null);
}
})
.catch((err: Error) => { // 错误回调
result.error("ERROR", err.message, null);
});
4.2 执行流程
extractTextFromDoc(filePath)
│
├── Promise resolve(text)
│ │
│ ├── text 不为空 → result.success(text)
│ │
│ └── text 为空 → result.error("UNAVAILABLE")
│
└── Promise reject(err)
│
└── result.error("ERROR", err.message)
4.3 为什么是异步的
// extractTextFromDoc 是 async 方法
private async extractTextFromDoc(filePath: string): Promise<string | null> {
// 内部有 await 操作
// 比如 zlib.decompressFile 是异步的
await zlib.decompressFile(filePath, tempDir);
// ...
}
| 操作 | 同步/异步 | 原因 |
|---|---|---|
| fs.openSync | 同步 | 系统 API 设计 |
| fs.readSync | 同步 | 系统 API 设计 |
| zlib.decompressFile | 异步 | 解压可能耗时 |
| 正则匹配 | 同步 | CPU 操作 |
| OLE2 解析 | 同步 | CPU 操作 |
📌 整个方法是异步的,主要因为
zlib.decompressFile是异步 API。虽然 .doc 解析全是同步操作,但为了统一接口,extractTextFromDoc 被声明为 async。
4.4 result 的延迟使用
// onMethodCall 返回后,result 仍然有效
onMethodCall(call: MethodCall, result: MethodResult): void {
// ...
this.extractTextFromDoc(filePath).then((text) => {
// 这里可能在 onMethodCall 返回之后才执行
result.success(text); // result 仍然有效
});
// onMethodCall 在这里返回,但 Promise 还没 resolve
}
这和 flutter_web_auth 的 authenticate 方法类似——result 被"保存"起来,等异步操作完成后再调用。不同的是:
| 维度 | doc_text | flutter_web_auth |
|---|---|---|
| 延迟时间 | 毫秒级(文件解析) | 秒到分钟级(用户操作) |
| result 存储 | Promise 闭包 | static callbacks Map |
| 取消机制 | 无 | cleanUpDanglingCalls |
五、result.notImplemented() 的防御
5.1 代码
} else {
result.notImplemented()
}
5.2 触发场景
| 场景 | 说明 |
|---|---|
| Dart 层调用了不存在的方法 | 代码错误 |
| 版本不匹配 | Dart 层新增了方法,原生层没更新 |
5.3 Dart 层收到的异常
try {
await methodChannel.invokeMethod('nonExistentMethod');
} on MissingPluginException catch (e) {
// "No implementation found for method nonExistentMethod on channel doc_text"
}
六、三种错误码
6.1 错误码清单
| 错误码 | 含义 | 触发条件 |
|---|---|---|
| INVALID_ARGUMENT | 参数无效 | filePath 为空 |
| UNAVAILABLE | 无法提取文本 | 解析返回 null |
| ERROR | 运行时错误 | Promise reject |
6.2 错误码的设计
// INVALID_ARGUMENT:参数问题,开发者的锅
result.error("INVALID_ARGUMENT", "filePath is required", null);
// UNAVAILABLE:文件问题,不是开发者的锅
result.error("UNAVAILABLE", "Could not extract text.", null);
// ERROR:未预期的错误
result.error("ERROR", err.message, null);
6.3 Dart 层处理
try {
final text = await DocText().extractTextFromDoc(filePath);
if (text != null) {
print('提取成功: $text');
}
} on PlatformException catch (e) {
switch (e.code) {
case 'INVALID_ARGUMENT':
print('参数错误: ${e.message}');
break;
case 'UNAVAILABLE':
print('无法提取文本(文件可能损坏或格式不支持)');
break;
case 'ERROR':
print('解析错误: ${e.message}');
break;
}
}
七、与 flutter_web_auth 的 onMethodCall 对比
7.1 代码结构对比
// doc_text:单方法 + 异步 Promise
onMethodCall(call: MethodCall, result: MethodResult): void {
if (call.method == "extractTextFromDoc") {
// 参数提取
// 异步调用 → then/catch
} else {
result.notImplemented()
}
}
// flutter_web_auth:双方法 + 混合同步/异步
onMethodCall(call: MethodCall, result: MethodResult): void {
if (call.method == "authenticate") {
// 参数提取
// 同步注册回调 + 异步等待深度链接
} else if (call.method == "cleanUpDanglingCalls") {
// 同步清理
} else {
result.notImplemented()
}
}
7.2 对比表
| 维度 | doc_text | flutter_web_auth |
|---|---|---|
| 方法数量 | 1 | 2 |
| 参数提取 | call.args → Map.get | call.argument() |
| 异步方式 | Promise.then/catch | callbacks Map + onNewWant |
| result 使用 | Promise 闭包中 | 存入 static Map |
| 取消支持 | ❌ | ✅ (cleanUpDanglingCalls) |
八、潜在的改进方向
8.1 添加进度回调
// 当前:只有最终结果
result.success(text);
// 改进:可以添加进度通知
// 比如大文件解析时通知 Dart 层进度
channel.invokeMethod("onProgress", {"percent": 50});
8.2 添加取消支持
// 当前:无法取消正在进行的解析
// 改进:支持取消
private currentTask: Promise<string | null> | null = null;
if (call.method == "cancel") {
// 取消当前任务
}
8.3 添加更多参数
// 当前
methodChannel.invokeMethod('extractTextFromDoc', {'filePath': filePath});
// 未来可能
methodChannel.invokeMethod('extractTextFromDoc', {
'filePath': filePath,
'encoding': 'utf-8', // 编码提示
'maxLength': 10000, // 最大提取长度
'includeHeaders': false, // 是否包含页眉页脚
});
📌 Map 参数格式为未来扩展留好了位置。这也是为什么用 Map 而不是直接传字符串的原因。
总结
doc_text 的 onMethodCall 实现简洁但完整:
- 单方法分发:只有 extractTextFromDoc
- Map 参数提取:call.args → Map.get(“filePath”)
- 空值校验:!filePath → INVALID_ARGUMENT
- 异步 Promise:then → success / catch → error
- 三种错误码:INVALID_ARGUMENT / UNAVAILABLE / ERROR
下一篇我们看文件格式路由——.doc 和 .docx 是怎么分流处理的。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
- MethodCall 文档
- MethodResult 文档
- doc_text Gitcode 仓库
- Flutter Platform Channels
- PlatformException
- ArkTS Promise
- JavaScript Map
- 开源鸿蒙跨平台社区

MethodChannel 方法调用与异步结果返回
更多推荐




所有评论(0)