前言

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

flutter_web_auth 的 onMethodCall 只需要处理两个方法:authenticatecleanUpDanglingCalls。authenticate 的参数提取和错误处理逻辑更复杂——因为它涉及到打开浏览器这个"跨进程"操作。

一、onMethodCall 完整实现

1.1 源码

onMethodCall(call: MethodCall, result: MethodResult): void {
  console.info(TAG, `onMethodCall: ${call.method}`);
  if (call.method == "authenticate") {
    const url = call.argument('url') as string;
    const callbackUrlScheme = call.argument('callbackUrlScheme') as string;
    console.info(TAG, `authenticate called with url=${url}, scheme=${callbackUrlScheme}`);
    this.authenticate(url, callbackUrlScheme, result);
  } else if (call.method == "cleanUpDanglingCalls") {
    this.cleanUpDanglingCalls(result);
  } else {
    result.notImplemented();
  }
}

1.2 方法分发表

方法名 参数 处理函数 返回值
authenticate url, callbackUrlScheme, preferEphemeral this.authenticate() 异步(等待回调)
cleanUpDanglingCalls this.cleanUpDanglingCalls() null
其他 - result.notImplemented() 错误

1.3 与 secure_application 的对比

维度 flutter_web_auth secure_application
方法数量 2个 5个
分发方式 if-else switch-case
默认处理 notImplemented() success(true)
异步方法 ✅ authenticate ❌ 全部同步

💡 flutter_web_auth 用 if-else 而不是 switch-case,因为只有两个方法,if-else 更简洁。secure_application 有五个方法,switch-case 更清晰。

二、authenticate 方法的参数提取

2.1 Dart 层传递的参数

// Dart 层
return await _channel.invokeMethod('authenticate', <String, dynamic>{
  'url': url,
  'callbackUrlScheme': callbackUrlScheme,
  'preferEphemeral': preferEphemeral ?? false,
});

2.2 原生层提取参数

const url = call.argument('url') as string;
const callbackUrlScheme = call.argument('callbackUrlScheme') as string;

2.3 参数对照表

Dart 参数 类型 原生提取 是否使用
url String call.argument('url') as string
callbackUrlScheme String call.argument('callbackUrlScheme') as string
preferEphemeral bool 未提取 ❌ 忽略

2.4 preferEphemeral 被忽略

// 当前实现中没有提取 preferEphemeral
// const preferEphemeral = call.argument('preferEphemeral') as boolean;

OpenHarmony 的 openLink API 不支持隐私浏览模式,所以 preferEphemeral 参数被静默忽略了。

📌 这是一个有意的设计决策,不是遗漏。如果未来 OpenHarmony 支持了隐私浏览,可以在这里提取并使用这个参数。

三、call.argument() 类型转换

3.1 类型安全

const url = call.argument('url') as string;

call.argument() 返回的是 ESObject 类型(类似 any),需要用 as 进行类型断言。

3.2 可能的类型问题

场景 Dart 传递 原生接收 结果
正常 'url': 'https://...' as string
null 值 'url': null as string null(不会崩溃)
缺少参数 没有 ‘url’ 键 as string null
类型错误 'url': 123 as string “123”(自动转换)

3.3 空值防御

// 当前实现没有做空值检查
const url = call.argument('url') as string;
// 如果 url 为 null,openLink(null) 会失败

// 更安全的写法
const url = call.argument('url') as string;
if (!url) {
  result.error("INVALID_ARGS", "url is required", null);
  return;
}

当前实现依赖 Dart 层的参数校验(required String url),所以原生层没有做额外的空值检查。这是合理的——Dart 的 required 关键字保证了参数不会为 null。

四、cleanUpDanglingCalls 的触发与清理

4.1 实现

private cleanUpDanglingCalls(result: MethodResult): void {
  FlutterWebAuthPlugin.callbacks.forEach((danglingResult: MethodResult) => {
    danglingResult.error("CANCELED", "User canceled login", null);
  });
  FlutterWebAuthPlugin.callbacks.clear();
  result.success(null);
}

4.2 执行流程

1. 遍历 callbacks Map 中所有 pending 的 MethodResult
2. 对每个 result 返回 CANCELED 错误
3. 清空整个 Map
4. 返回 success(null) 给 Dart 层的 _cleanUpDanglingCalls 调用

4.3 forEach 的参数

FlutterWebAuthPlugin.callbacks.forEach((danglingResult: MethodResult) => {
  danglingResult.error("CANCELED", "User canceled login", null);
});

Map 的 forEach 回调参数是 (value, key, map)。这里只用了 value(MethodResult),key(scheme)被忽略了。

4.4 error 参数

danglingResult.error("CANCELED", "User canceled login", null);
//                    ^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^   ^^^^
//                    错误码      错误消息                 详情
参数 Dart 层对应
code “CANCELED” PlatformException.code
message “User canceled login” PlatformException.message
details null PlatformException.details

4.5 Dart 层的捕获

try {
  final result = await FlutterWebAuth.authenticate(...);
} on PlatformException catch (e) {
  if (e.code == 'CANCELED') {
    // 用户取消了登录
    print('用户取消: ${e.message}');
  }
}

五、result.notImplemented() 的防御处理

5.1 实现

} else {
  result.notImplemented();
}

5.2 触发场景

场景 说明
Dart 层调用了不存在的方法 代码错误
版本不匹配 Dart 层新增了方法,原生层没更新
其他插件的消息误发 通道名称冲突(极罕见)

5.3 与 secure_application 的对比

// flutter_web_auth:返回 notImplemented
result.notImplemented();

// secure_application:返回 success(true)
default:
  result.success(true);
  break;
处理方式 含义 适用场景
notImplemented() 明确告诉调用者"这个方法不存在" 严格的 API 契约
success(true) 静默忽略未知方法 宽松的兼容策略

💡 flutter_web_auth 选择了更严格的方式。如果 Dart 层调用了不存在的方法,会收到 MissingPluginException,开发者能立即发现问题。

六、authenticate 方法的异步特性

6.1 与其他方法的区别

// authenticate:不立即返回结果
this.authenticate(url, callbackUrlScheme, result);
// result 被存入 callbacks Map,等待 onNewWant 时才调用 result.success()

// cleanUpDanglingCalls:立即返回结果
this.cleanUpDanglingCalls(result);
// result.success(null) 在方法内部立即调用

6.2 MethodResult 的延迟使用

方法 result 使用时机 延迟
authenticate onNewWant 回调时 秒到分钟
cleanUpDanglingCalls 方法内部 即时

6.3 延迟使用的风险

风险:如果 result 在使用前被 GC 回收怎么办?

答:不会。callbacks Map 持有 result 的引用,
    只要 Map 中有这个 entry,result 就不会被 GC。
    
    callbacks.set(scheme, result);  // Map 持有引用
    // ... 等待 ...
    callbacks.get(scheme);          // 引用仍然有效

七、日志记录

7.1 当前的日志点

onMethodCall(call: MethodCall, result: MethodResult): void {
  console.info(TAG, `onMethodCall: ${call.method}`);
  if (call.method == "authenticate") {
    console.info(TAG, `authenticate called with url=${url}, scheme=${callbackUrlScheme}`);
    // ...
  }
}

7.2 日志级别使用

操作 级别 示例
方法调用 info onMethodCall: authenticate
参数记录 info url=..., scheme=...
成功操作 info openLink succeeded
失败操作 error openLink failed: ...

7.3 安全注意事项

// ⚠️ 当前实现会把完整 URL 打印到日志中
console.info(TAG, `authenticate called with url=${url}`);

// URL 中可能包含 client_id 等敏感信息
// 生产环境建议只打印 Scheme,不打印完整 URL
console.info(TAG, `authenticate called with scheme=${callbackUrlScheme}`);

📌 生产环境中应该避免在日志中打印完整的认证 URL,因为 URL 中可能包含 client_id、state 等敏感参数。

总结

本文分析了 flutter_web_auth 的 onMethodCall 实现:

  1. 两个方法:authenticate(异步)和 cleanUpDanglingCalls(同步)
  2. 参数提取:call.argument() + as 类型断言
  3. preferEphemeral 忽略:OpenHarmony 不支持隐私浏览
  4. notImplemented():严格的 API 契约,未知方法报错
  5. 日志安全:生产环境避免打印完整 URL

下一篇我们讲 Dangling Calls 清理机制——用户取消认证时的完整处理流程。

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


相关资源:

请添加图片描述

MethodChannel 方法分发架构

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐