前言

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

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

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

1.1 类声明

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 代码

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 参数

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

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

三、onDetachedFromEngine:插件清理

3.1 代码

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 检查

if (this.channel != null) {
  this.channel.setMethodCallHandler(null)
}

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

四、getUniqueClassName:插件唯一标识

4.1 代码

getUniqueClassName(): string {
  return "DocTextPlugin"
}

4.2 作用

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

4.3 命名规范

// ✅ 推荐:与类名一致
getUniqueClassName(): string {
  return "DocTextPlugin"
}

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

五、constructor:空构造函数

5.1 代码

constructor() {
}

5.2 为什么是空的

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

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

5.3 与 flutter_web_auth 的对比

// doc_text:空构造函数
constructor() {
}

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

六、成员变量分析

6.1 doc_text 的成员变量

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

6.2 flutter_web_auth 的成员变量

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 接口定义

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 接口

interface MethodCallHandler {
  onMethodCall(call: MethodCall, result: MethodResult): void;
}

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

八、插件注册流程

8.1 自动注册

// 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 手动注册(不推荐)

// 如果自动注册不工作,可以手动注册
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 通信架构

Logo

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

更多推荐