Flutter三方库适配OpenHarmony【flutter_web_auth】— Android 端 Chrome Custom Tabs 实现分析

前言

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

在适配 OpenHarmony 之前,先看看 Android 端是怎么做的。Android 用 Chrome Custom Tabs 打开认证页面,用 CallbackActivity + Intent Filter 接收回调。这套方案是 OpenHarmony 适配的主要参照物------很多设计决策都是从 Android 映射过来的。

一、Chrome Custom Tabs 与 WebView 的区别

1.1 为什么不用 WebView

用户可能已经在 Chrome 中登录了 Google 账号。如果用 Chrome Custom Tabs,用户不需要重新输入密码------直接点击"允许"就行。这对用户体验的提升是巨大的。

复制代码
WebView:用户每次都要输入用户名和密码
Chrome Custom Tabs:用户可能只需要点一下"允许"

💡 这也是 OpenHarmony 用系统浏览器(openLink)而不是 WebView 的原因------系统浏览器共享用户的登录状态。

二、CallbackActivity 的 Intent Filter 配置

2.1 AndroidManifest.xml 配置

xml 复制代码
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" 
          android:exported="true">
  <intent-filter android:label="flutter_web_auth">
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="YOUR_CALLBACK_URL_SCHEME_HERE" />
  </intent-filter>
</activity>

2.2 各字段含义

字段 作用
action.VIEW 查看操作 表示这个 Activity 可以处理"查看"类型的 Intent
category.DEFAULT 默认类别 允许隐式 Intent 启动
category.BROWSABLE 可浏览 允许从浏览器启动
data.scheme 自定义 Scheme 匹配特定 URL Scheme

2.3 工作流程

复制代码
1. 浏览器重定向到 myapp://callback?code=abc123
2. Android 系统查找能处理 myapp:// 的 Activity
3. 找到 CallbackActivity(因为 Intent Filter 匹配)
4. CallbackActivity 启动,获取 Intent 中的 URI
5. CallbackActivity 把 URI 传给 FlutterWebAuthPlugin
6. Plugin 通过 MethodChannel 返回给 Dart

2.4 与 OpenHarmony 的对应关系

Android OpenHarmony
CallbackActivity EntryAbility(复用主 Ability)
Intent Filter skills 配置
android:scheme uris.scheme
action.VIEW ohos.want.action.viewData
category.BROWSABLE entity.system.browsable

三、FLAG_ACTIVITY_NO_HISTORY 与 preferEphemeral

3.1 preferEphemeral 在 Android 上的实现

kotlin 复制代码
// Android 端代码(简化)
val intent = CustomTabsIntent.Builder().build()
if (preferEphemeral) {
    intent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
}
intent.launchUrl(context, Uri.parse(url))

3.2 FLAG_ACTIVITY_NO_HISTORY 的效果

有 FLAG 无 FLAG
认证页面不保留在任务栈中 认证页面保留在任务栈中
用户按返回键直接回到 App 用户按返回键回到认证页面
不留浏览历史 留浏览历史

3.3 在 OpenHarmony 上的对应

typescript 复制代码
// OpenHarmony 当前实现:没有处理 preferEphemeral
const url = call.argument('url') as string;
// preferEphemeral 参数被忽略了
context.openLink(url);

📌 当前限制:OpenHarmony 的 openLink API 不支持"不留历史"的选项。preferEphemeral 参数在 OpenHarmony 上没有效果。未来可以通过 Want 的 flags 参数来实现类似功能。

四、从浏览器回调到 Flutter 的完整数据流

4.1 Android 数据流

复制代码
浏览器重定向
    ↓
Android 系统匹配 Intent Filter
    ↓
CallbackActivity.onCreate(intent)
    ↓
intent.data → URI 字符串
    ↓
FlutterWebAuthPlugin.callbacks[scheme].success(uri)
    ↓
MethodChannel → Dart
    ↓
FlutterWebAuth.authenticate() 返回 uri

4.2 OpenHarmony 数据流

复制代码
浏览器重定向
    ↓
OpenHarmony 系统匹配 Want skills
    ↓
EntryAbility.onNewWant(want)
    ↓
FlutterWebAuthPlugin.onNewWant(want)
    ↓
want.uri → URI 字符串
    ↓
FlutterWebAuthPlugin.callbacks[scheme].success(uri)
    ↓
MethodChannel → Dart
    ↓
FlutterWebAuth.authenticate() 返回 uri

4.3 关键差异

差异点 Android OpenHarmony
回调接收者 独立的 CallbackActivity 复用 EntryAbility
数据来源 intent.data want.uri
触发方式 Activity 自动启动 onNewWant 回调
需要宿主配合 只需配置 Manifest 需要写代码调用 onNewWant

五、Android 端 dangling calls 清理

5.1 问题场景

复制代码
1. 用户点击登录 → 浏览器打开
2. 用户在浏览器中按返回键 → 回到 App
3. authenticate() 的 Future 还在等待 → 永远不会完成

5.2 Android 的清理机制

kotlin 复制代码
// Android 端
fun cleanUpDanglingCalls() {
    callbacks.forEach { (_, result) ->
        result.error("CANCELED", "User canceled login", null)
    }
    callbacks.clear()
}

5.3 触发时机

复制代码
App 回到前台(resumed)
    ↓
Dart: _OnAppLifecycleResumeObserver.didChangeAppLifecycleState(resumed)
    ↓
Dart: _cleanUpDanglingCalls()
    ↓
Dart → Native: invokeMethod('cleanUpDanglingCalls')
    ↓
Native: 遍历 callbacks,返回 CANCELED

5.4 OpenHarmony 的实现

typescript 复制代码
// OpenHarmony 端:完全相同的逻辑
private cleanUpDanglingCalls(result: MethodResult): void {
  FlutterWebAuthPlugin.callbacks.forEach((danglingResult: MethodResult) => {
    danglingResult.error("CANCELED", "User canceled login", null);
  });
  FlutterWebAuthPlugin.callbacks.clear();
  result.success(null);
}

💡 这是 Dart 层驱动的清理机制 。不管原生端是 Android 还是 OpenHarmony,清理逻辑都是 Dart 层的 Observer 触发的。原生端只需要实现 cleanUpDanglingCalls 方法就行。

六、Result 生命周期管理

6.1 MethodResult 的生命周期

复制代码
创建:onMethodCall 中接收 result 参数
    ↓
存储:callbacks[scheme] = result
    ↓
使用:result.success(uri) 或 result.error(...)
    ↓
销毁:从 callbacks Map 中删除

6.2 一个 Result 只能用一次

typescript 复制代码
// ✅ 正确:用完就删
const pendingResult = FlutterWebAuthPlugin.callbacks.get(scheme);
FlutterWebAuthPlugin.callbacks.delete(scheme);  // 先删
pendingResult.success(uri);                      // 再用

// ❌ 错误:重复使用
pendingResult.success(uri);
pendingResult.success(uri);  // 第二次调用会崩溃

6.3 防止 Result 泄漏

场景 处理方式
正常回调 onNewWant 中 success + delete
用户取消 cleanUpDanglingCalls 中 error + clear
插件销毁 onDetachedFromEngine 中不处理(Dart 层会清理)

七、Android 实现的设计启示

7.1 对 OpenHarmony 适配的启示

Android 设计 OpenHarmony 对应 启示
CallbackActivity EntryAbility.onNewWant 回调入口
Intent Filter skills 配置 深度链接匹配
static callbacks static callbacks 跨实例数据共享
cleanUpDanglingCalls cleanUpDanglingCalls 用户取消处理

7.2 OpenHarmony 的额外挑战

  1. 没有独立的 CallbackActivity:必须在 EntryAbility 中手动集成
  2. openLink 是异步的:Android 的 startActivity 是同步的
  3. 需要降级方案:openLink 失败时要 fallback 到 startAbility

总结

本文分析了 Android 端的 Chrome Custom Tabs 实现:

  1. Chrome Custom Tabs:共享 Cookie、安全沙箱、用户信任度高
  2. CallbackActivity:通过 Intent Filter 捕获深度链接回调
  3. preferEphemeral:FLAG_ACTIVITY_NO_HISTORY 不留浏览历史
  4. dangling calls:Dart 层 Observer 驱动的清理机制
  5. Result 生命周期:一次性使用,用完即删

下一篇我们看 iOS/macOS 端的 ASWebAuthenticationSession 实现。

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


相关资源:

相关推荐
zh_xuan3 小时前
kotlin Channel的用法
android·kotlin·协程·channel
zh_xuan3 小时前
kotlin Flow的用法
android·开发语言·kotlin·协程·flow
松叶似针5 小时前
Flutter三方库适配OpenHarmony【secure_application】— 总结回顾与隐私保护技术展望
flutter
huohaiyu7 小时前
从URL到页面的完整解析流程
前端·网络·chrome·url
普通网友10 小时前
Android Jetpack 架构组件最佳实践之“网抑云”APP
android·架构·android jetpack
普通网友10 小时前
原创_Android Jetpack Compose 最全上手指南
android·android jetpack
FDoubleman10 小时前
Android Jetpack之Compose入门(一)
android·android jetpack
普通网友10 小时前
Android Jetpack从入门到精通,干货满满
android·android jetpack