Flutter三方库适配OpenHarmony【doc_text】— onMethodCall 分发与文件路径参数提取

前言

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

doc_text 的 onMethodCall 只处理一个方法:extractTextFromDoc。但它的参数提取方式不太一样------用的是 call.args 而不是 call.argument(),而且整个方法调用是异步的,需要用 Promise 链来处理结果。

一、onMethodCall 完整实现

1.1 源码

typescript 复制代码
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()

typescript 复制代码
// 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 复制代码
// Dart 层
methodChannel.invokeMethod<String>(
  'extractTextFromDoc',
  {'filePath': filePath},  // Map<String, dynamic>
);
typescript 复制代码
// 原生层接收
const args = call.args as Map<string, Object>;
// args = Map { "filePath" → "/data/storage/test.docx" }

2.4 为什么用 Map 而不是直接传字符串

dart 复制代码
// 方案1:直接传字符串(更简单)
methodChannel.invokeMethod<String>('extractTextFromDoc', filePath);

// 方案2:用 Map 包装(当前实现)
methodChannel.invokeMethod<String>('extractTextFromDoc', {'filePath': filePath});
方案 优点 缺点
直接传字符串 简单 不可扩展,未来加参数要改接口
Map 包装 可扩展,未来加参数不影响 多一层解包

💡 用 Map 包装是更好的实践。如果未来需要添加参数(比如编码提示、页码范围),只需要在 Map 中加新的 key,不需要改方法签名。

三、空值校验

3.1 代码

typescript 复制代码
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 的对比

typescript 复制代码
// 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 代码结构

typescript 复制代码
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 为什么是异步的

typescript 复制代码
// 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 的延迟使用

typescript 复制代码
// 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 代码

typescript 复制代码
} else {
  result.notImplemented()
}

5.2 触发场景

场景 说明
Dart 层调用了不存在的方法 代码错误
版本不匹配 Dart 层新增了方法,原生层没更新

5.3 Dart 层收到的异常

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 错误码的设计

typescript 复制代码
// 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 层处理

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 代码结构对比

typescript 复制代码
// 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 添加进度回调

typescript 复制代码
// 当前:只有最终结果
result.success(text);

// 改进:可以添加进度通知
// 比如大文件解析时通知 Dart 层进度
channel.invokeMethod("onProgress", {"percent": 50});

8.2 添加取消支持

typescript 复制代码
// 当前:无法取消正在进行的解析
// 改进:支持取消
private currentTask: Promise<string | null> | null = null;

if (call.method == "cancel") {
  // 取消当前任务
}

8.3 添加更多参数

dart 复制代码
// 当前
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 方法调用与异步结果返回

相关推荐
卢叁2 小时前
Flutter之路由监听器
前端·flutter
恋猫de小郭2 小时前
Android 17 有什么需要适配的?2026 Android 禁止侧载又是什么?
android·前端·flutter
阿林来了2 小时前
Flutter三方库适配OpenHarmony【flutter_web_auth】— EntryAbility 深度链接回调集成
flutter
阿林来了2 小时前
Flutter三方库适配OpenHarmony【flutter_web_auth】— OpenHarmony 插件工程搭建与配置文件详解
flutter·harmonyos
2601_949593652 小时前
Flutter for Harmony 跨平台开发实战:递归分形树——L-系统的生长逻辑
flutter
lqj_本人2 小时前
Flutter三方库适配OpenHarmony【apple_product_name】用户反馈系统集成设备信息
flutter
2601_949593652 小时前
Flutter for Harmony 跨平台开发实战:流场与矢量可视化——不可见力量的轨迹追踪
flutter
早點睡3902 小时前
Flutter for Harmony 跨平台开发实战:光线追踪——折射反射的光学模拟
flutter
早點睡3903 小时前
Flutter for Harmony 跨平台开发实战:鸿蒙与音乐律动艺术、FFT 频谱能量场:正弦函数的叠加艺术
flutter·华为·harmonyos