前言

欢迎加入开源鸿蒙跨平台社区: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 实现简洁但完整:

  1. 单方法分发:只有 extractTextFromDoc
  2. Map 参数提取:call.args → Map.get(“filePath”)
  3. 空值校验:!filePath → INVALID_ARGUMENT
  4. 异步 Promise:then → success / catch → error
  5. 三种错误码:INVALID_ARGUMENT / UNAVAILABLE / ERROR

下一篇我们看文件格式路由——.doc 和 .docx 是怎么分流处理的。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

请添加图片描述

MethodChannel 方法调用与异步结果返回

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐