Flutter三方库适配OpenHarmony【doc_text】— FlutterPlugin 接口实现与 MethodChannel 注册

前言

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

doc_text 的 DocTextPlugin 只实现了两个接口:FlutterPluginMethodCallHandler。不需要 AbilityAware------因为提取文档文本不需要 UIAbilityContext,只需要文件系统 API。这个"少一个接口"的差异背后,反映的是插件功能对系统能力的不同需求。

一、接口选择:两个就够了

1.1 类声明

typescript 复制代码
export default class DocTextPlugin implements FlutterPlugin, MethodCallHandler {
  private channel: MethodChannel | null = null;

  constructor() {
  }
  // ...
}

1.2 接口对比

1.3 为什么不需要 AbilityAware

复制代码
AbilityAware 提供的能力:
- onAttachedToAbility(ability) → 获取 UIAbility 引用
- UIAbility.context → UIAbilityContext
- UIAbilityContext 可以做:openLink、startAbility、requestPermissions 等

doc_text 需要的能力:
- 读文件 → fs.openSync(不需要 Context)
- 解压 ZIP → zlib.decompressFile(不需要 Context)
- 创建临时目录 → fs.mkdirSync(不需要 Context)

结论:doc_text 的所有操作都不需要 UIAbilityContext

💡 判断是否需要 AbilityAware 的简单规则 :如果你的插件需要调用 context.xxx() 的方法(打开浏览器、启动 Ability、请求权限等),就需要 AbilityAware。如果只是做数据处理、文件操作、计算,就不需要。

二、onAttachedToEngine:插件初始化

2.1 代码

typescript 复制代码
onAttachedToEngine(binding: FlutterPluginBinding): void {
  this.channel = new MethodChannel(binding.getBinaryMessenger(), "doc_text");
  this.channel.setMethodCallHandler(this)
}

2.2 执行时机

复制代码
Flutter 引擎启动
    ↓
GeneratedPluginRegistrant.registerWith(flutterEngine)
    ↓
DocTextPlugin 被实例化
    ↓
onAttachedToEngine(binding) 被调用
    ↓
MethodChannel 创建,Handler 注册
    ↓
准备就绪,等待 Dart 层调用

2.3 两行代码做了什么

代码 作用
1 new MethodChannel(messenger, "doc_text") 创建通道,名称 "doc_text"
2 channel.setMethodCallHandler(this) 把自己注册为消息处理器

2.4 binding 参数

typescript 复制代码
// FlutterPluginBinding 提供的能力
binding.getBinaryMessenger()  // 二进制消息通道 → 创建 MethodChannel
binding.getApplicationContext()  // 应用上下文(非 UIAbilityContext)
binding.getFlutterAssets()  // Flutter 资源路径

doc_text 只用了 getBinaryMessenger()getApplicationContext() 虽然可用,但 doc_text 不需要------文件路径由 Dart 层传入,不需要通过 Context 获取。

三、onDetachedFromEngine:插件清理

3.1 代码

typescript 复制代码
onDetachedFromEngine(binding: FlutterPluginBinding): void {
  if (this.channel != null) {
    this.channel.setMethodCallHandler(null)
  }
}

3.2 为什么要置空 Handler

复制代码
如果不置空:
1. 引擎已经 detach
2. Dart 层不再发送消息
3. 但 MethodChannel 对象仍然持有 DocTextPlugin 的引用
4. DocTextPlugin 无法被 GC 回收
5. 内存泄漏

置空后:
1. MethodChannel 不再持有 Handler 引用
2. DocTextPlugin 可以被 GC 回收
3. 没有内存泄漏

3.3 null 检查

typescript 复制代码
if (this.channel != null) {
  this.channel.setMethodCallHandler(null)
}

理论上 onDetachedFromEngine 一定在 onAttachedToEngine 之后调用,所以 this.channel 不应该为 null。但加上 null 检查是防御性编程的好习惯。

四、getUniqueClassName:插件唯一标识

4.1 代码

typescript 复制代码
getUniqueClassName(): string {
  return "DocTextPlugin"
}

4.2 作用

用途 说明
插件注册 Flutter 引擎用这个名称标识插件
防重复注册 如果两个插件返回相同的名称,会冲突
日志标识 框架内部日志中使用

4.3 命名规范

typescript 复制代码
// ✅ 推荐:与类名一致
getUniqueClassName(): string {
  return "DocTextPlugin"
}

// ❌ 不推荐:与类名不一致
getUniqueClassName(): string {
  return "MyPlugin"  // 容易混淆
}

五、constructor:空构造函数

5.1 代码

typescript 复制代码
constructor() {
}

5.2 为什么是空的

doc_text 的所有初始化都在 onAttachedToEngine 中完成。构造函数不做任何事情。

复制代码
插件生命周期:
1. constructor() → 对象创建(空)
2. onAttachedToEngine() → 初始化 MethodChannel
3. onMethodCall() → 处理方法调用(多次)
4. onDetachedFromEngine() → 清理资源

5.3 与 flutter_web_auth 的对比

typescript 复制代码
// doc_text:空构造函数
constructor() {
}

// flutter_web_auth:也是空构造函数,但有额外的成员变量
private ability: UIAbility | null = null;  // 需要在 onAttachedToAbility 中赋值

六、成员变量分析

6.1 doc_text 的成员变量

typescript 复制代码
export default class DocTextPlugin implements FlutterPlugin, MethodCallHandler {
  private channel: MethodChannel | null = null;
  // 就这一个
}

6.2 flutter_web_auth 的成员变量

typescript 复制代码
export default class FlutterWebAuthPlugin implements FlutterPlugin, MethodCallHandler, AbilityAware {
  private channel: MethodChannel | null = null;
  private ability: UIAbility | null = null;
  private static callbacks: Map<string, MethodResult> = new Map();
  // 三个,其中一个是 static
}

6.3 对比

变量 doc_text flutter_web_auth 用途
channel MethodChannel 引用
ability UIAbility 引用
static callbacks 异步回调存储

📌 doc_text 只有一个成员变量,因为它的操作是同步的(在 Promise 内部完成)------不需要跨方法调用保存状态。flutter_web_auth 需要 static callbacks 是因为 authenticate 和 onNewWant 是两个不同时机的操作。

七、FlutterPlugin 接口的完整契约

7.1 接口定义

typescript 复制代码
interface FlutterPlugin {
  getUniqueClassName(): string;
  onAttachedToEngine(binding: FlutterPluginBinding): void;
  onDetachedFromEngine(binding: FlutterPluginBinding): void;
}

7.2 生命周期保证

方法 调用次数 时机
constructor 1次 插件实例化
onAttachedToEngine 1次 引擎绑定
onMethodCall 0-N次 Dart 层调用
onDetachedFromEngine 1次 引擎解绑

7.3 MethodCallHandler 接口

typescript 复制代码
interface MethodCallHandler {
  onMethodCall(call: MethodCall, result: MethodResult): void;
}

只有一个方法,所有的方法分发逻辑都在这里面。

八、插件注册流程

8.1 自动注册

typescript 复制代码
// GeneratedPluginRegistrant.ets(自动生成)
import DocTextPlugin from 'doc_text';

export class GeneratedPluginRegistrant {
  static registerWith(flutterEngine: FlutterEngine): void {
    flutterEngine.getPlugins().add(new DocTextPlugin());
    // 其他插件...
  }
}

8.2 注册链路

复制代码
FlutterAbility.configureFlutterEngine(engine)
    ↓
GeneratedPluginRegistrant.registerWith(engine)
    ↓
engine.getPlugins().add(new DocTextPlugin())
    ↓
DocTextPlugin.constructor()
    ↓
DocTextPlugin.onAttachedToEngine(binding)
    ↓
MethodChannel 创建完成,插件就绪

8.3 手动注册(不推荐)

typescript 复制代码
// 如果自动注册不工作,可以手动注册
configureFlutterEngine(flutterEngine: FlutterEngine) {
  super.configureFlutterEngine(flutterEngine);
  GeneratedPluginRegistrant.registerWith(flutterEngine);
  // 手动添加(通常不需要)
  flutterEngine.getPlugins().add(new DocTextPlugin());
}

总结

doc_text 的接口实现非常简洁:

  1. 两个接口:FlutterPlugin + MethodCallHandler(不需要 AbilityAware)
  2. 一个成员变量:只有 MethodChannel
  3. 空构造函数:所有初始化在 onAttachedToEngine 中
  4. 清理逻辑:onDetachedFromEngine 中置空 Handler
  5. 零系统能力依赖:不需要 UIAbilityContext

下一篇我们看 onMethodCall 的实现------参数提取和异步处理。

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


相关资源:

FlutterPlugin 接口与 MethodChannel 通信架构

相关推荐
lqj_本人2 小时前
Flutter三方库适配OpenHarmony【apple_product_name】插件架构设计解析
flutter
星空22232 小时前
鸿蒙跨平台实战day48:React Native在OpenHarmony上的Font字体加载管理详解
react native·华为·harmonyos
wangyang62752 小时前
Xcode 26 真机运行崩溃 EXC_BAD_ACCESS map_images_nolock 完美解决方案
flutter·ios
星空22232 小时前
鸿蒙跨平台实战day46:React Native在OpenHarmony上的AccessibilityInfo无障碍检测
react native·react.js·harmonyos
●VON2 小时前
HarmonyOS应用开发实战(基础篇)Day12 -《打造专业级底部导航栏》
学习·华为·harmonyos·von
2601_949593652 小时前
Flutter for Harmony 跨平台开发实战:超形状与超椭圆——参数方程的形态边界
flutter
特立独行的猫a2 小时前
跨平台开发实战:uni-app x 鸿蒙HarmonyOS网络模块封装与轮播图实现
android·网络·uni-app·harmonyos·轮播图·uni-app-x
coooliang2 小时前
【鸿蒙 NEXT】自定义dialog
华为·harmonyos
Swift社区2 小时前
Flutter 中如何优雅地处理复杂表单
前端·flutter·前端框架