适合谁看

  • 正在读 EntryAbility.ets 的开发者

  • 想理解鸿蒙 Flutter 启动链的人

  • 已经接入插件,但还没理清冷启动参数的人

问题背景

很多开发者第一次看 EntryAbility 时,会把它误认为只是"应用启动入口"。但在真实项目里,它通常至少还承担三件额外事情:

  • 配置 Flutter 引擎

  • 注册原生插件

  • 承接系统传入参数

如果不先把这三层分清,后面看插件和入口时会很乱。

项目中的真实场景

食界探味当前的 EntryAbility.ets 完整代码:

import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { AbilityConstant, Want } from '@kit.AbilityKit';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import SpeechRecognitionPlugin from '../plugins/SpeechRecognitionPlugin';
import TextToSpeechPlugin from '../plugins/TextToSpeechPlugin';
import IntentNavigationPlugin from '../plugins/IntentNavigationPlugin';
import AntiPeepProtectionPlugin from '../plugins/AntiPeepProtectionPlugin';
import window from '@ohos.window';

export default class EntryAbility extends FlutterAbility {
  configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    flutterEngine.getPlugins()?.add(new SpeechRecognitionPlugin())
    flutterEngine.getPlugins()?.add(new TextToSpeechPlugin())
    flutterEngine.getPlugins()?.add(new IntentNavigationPlugin())
    flutterEngine.getPlugins()?.add(new AntiPeepProtectionPlugin())
  }

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    super.onCreate(want, launchParam)
    const pageId = want.parameters?.['pageId'] as string;
    const dishId = want.parameters?.['dishId'] as string;
    if (pageId) {
      IntentNavigationPlugin.setPendingNavigation(pageId, dishId);
    }
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    super.onNewWant(want, launchParam)
    const pageId = want.parameters?.['pageId'] as string;
    const dishId = want.parameters?.['dishId'] as string;
    if (pageId) {
      const plugin = IntentNavigationPlugin.getInstance();
      if (plugin) {
        plugin.navigateToPage(pageId, dishId);
      } else {
        IntentNavigationPlugin.setPendingNavigation(pageId, dishId);
      }
    }
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    super.onWindowStageCreate(windowStage)
    windowStage.getMainWindow().then((mainWindow: window.Window) => {
      mainWindow.setWindowLayoutFullScreen(true)
      mainWindow.setWindowSystemBarEnable([])
    }).catch((err: Error) => {
      console.error(`Failed to enable immersive window: ${JSON.stringify(err)}`)
    })
  }
}

核心实现

一、EntryAbility 的 4 个生命周期方法

方法

触发时机

职责

configureFlutterEngine

Flutter 引擎配置时

注册插件

onCreate

应用冷启动时

承接冷启动参数

onNewWant

应用运行中收到新 Intent 时

承接运行期入口

onWindowStageCreate

窗口创建时

初始化窗口环境

二、configureFlutterEngine——把"能力前提"先准备好

configureFlutterEngine(flutterEngine: FlutterEngine) {
  super.configureFlutterEngine(flutterEngine)
  GeneratedPluginRegistrant.registerWith(flutterEngine)
  flutterEngine.getPlugins()?.add(new SpeechRecognitionPlugin())
  flutterEngine.getPlugins()?.add(new TextToSpeechPlugin())
  flutterEngine.getPlugins()?.add(new IntentNavigationPlugin())
  flutterEngine.getPlugins()?.add(new AntiPeepProtectionPlugin())
}

这个阶段做了两件事:

1. 生态插件自动注册

GeneratedPluginRegistrant.registerWith(flutterEngine)

GeneratedPluginRegistrant 会自动注册 pubspec.yaml 中声明的 Flutter 插件。

2. 项目自定义插件手动注册

flutterEngine.getPlugins()?.add(new SpeechRecognitionPlugin())
flutterEngine.getPlugins()?.add(new TextToSpeechPlugin())
flutterEngine.getPlugins()?.add(new IntentNavigationPlugin())
flutterEngine.getPlugins()?.add(new AntiPeepProtectionPlugin())

这 4 个插件是食界探味自己写的鸿蒙原生插件,需要手动 add 进去。

为什么要手动注册:

插件

通道名

作用

SpeechRecognitionPlugin

com.foodvoyage.speech_recognition

语音识别

TextToSpeechPlugin

com.foodvoyage.text_to_speech

TTS

IntentNavigationPlugin

com.foodvoyage.intent_navigation

Intent 导航

AntiPeepProtectionPlugin

com.foodvoyage.anti_peep_protection

防窥保护

如果漏注册会怎样:

漏注册 SpeechRecognitionPlugin
  → Flutter 调用 SpeechRecognitionChannel.startListening()
  → MissingPluginException
  → 语音识别静默失效

漏注册 IntentNavigationPlugin
  → Flutter 调用 IntentNavigationChannel.init()
  → 通道不存在
  → 系统入口跳转全部失效

三、onCreate——承接"冷启动参数"

onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  super.onCreate(want, launchParam)
  const pageId = want.parameters?.['pageId'] as string;
  const dishId = want.parameters?.['dishId'] as string;
  if (pageId) {
    IntentNavigationPlugin.setPendingNavigation(pageId, dishId);
  }
}

冷启动时,系统通过 want.parameters 传入启动参数。onCreate 读取这些参数后,调用 setPendingNavigation 暂存。

为什么不能在 onCreate 里直接写页面跳转:

原因

说明

Flutter 引擎未 ready

onCreate 时 Flutter 可能还在启动

GoRouter 未初始化

路由系统还没准备好

插件实例未创建

IntentNavigationPlugin.getInstance() 返回 null

所以只能先暂存为 pending,等 Flutter 初始化后消费。

pending 机制的工作原理:

onCreate(want)
  → 提取 pageId, dishId
  → IntentNavigationPlugin.setPendingNavigation(pageId, dishId)
  → pendingNavigation = { pageId, dishId }

Flutter 初始化后
  → IntentNavigationChannel.init()
  → _consumePending()
  → invokeMethod('consumePendingNavigation')
  → 拿到 { pageId, dishId }
  → _navigate() → 路由跳转

四、onNewWant——承接"运行中的新入口请求"

onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  super.onNewWant(want, launchParam)
  const pageId = want.parameters?.['pageId'] as string;
  const dishId = want.parameters?.['dishId'] as string;
  if (pageId) {
    const plugin = IntentNavigationPlugin.getInstance();
    if (plugin) {
      plugin.navigateToPage(pageId, dishId);
    } else {
      IntentNavigationPlugin.setPendingNavigation(pageId, dishId);
    }
  }
}

onNewWant 处理的是"应用已存活时再次收到入口请求"的场景。

onCreate 的区别:

方法

场景

处理方式

onCreate

冷启动

只能暂存 pending

onNewWant

热启动

先尝试直接推送,失败则暂存

为什么 onNewWant 可以尝试直接推送:

应用已在后台运行
  → Flutter 引擎已经创建
  → IntentNavigationPlugin 已经注册
  → getInstance() 可能返回实例
  → 可以尝试 navigateToPage()

但仍然需要 fallback 到 pending,因为 Flutter 可能还没完全恢复。

五、onWindowStageCreate——把"显示环境"先收好

onWindowStageCreate(windowStage: window.WindowStage): void {
  super.onWindowStageCreate(windowStage)
  windowStage.getMainWindow().then((mainWindow: window.Window) => {
    mainWindow.setWindowLayoutFullScreen(true)
    mainWindow.setWindowSystemBarEnable([])
  }).catch((err: Error) => {
    console.error(`Failed to enable immersive window: ${JSON.stringify(err)}`)
  })
}

这段代码设置了全屏布局和隐藏系统栏。虽然不是插件注册和参数传递本身,但它说明了一件很重要的事:

EntryAbility 不是单纯的"启动回调集合",它也是 Flutter 容器运行环境的第一层配置点。

六、完整的启动链时序图

鸿蒙系统启动应用
  │
  ▼
EntryAbility.onCreate(want)
  ├─ 读取 pageId, dishId
  ├─ IntentNavigationPlugin.setPendingNavigation(pageId, dishId)
  │
  ▼
EntryAbility.configureFlutterEngine(flutterEngine)
  ├─ GeneratedPluginRegistrant.registerWith(flutterEngine)
  ├─ flutterEngine.getPlugins()?.add(SpeechRecognitionPlugin)
  ├─ flutterEngine.getPlugins()?.add(TextToSpeechPlugin)
  ├─ flutterEngine.getPlugins()?.add(IntentNavigationPlugin)
  ├─ flutterEngine.getPlugins()?.add(AntiPeepProtectionPlugin)
  │
  ▼
EntryAbility.onWindowStageCreate(windowStage)
  ├─ setWindowLayoutFullScreen(true)
  ├─ setWindowSystemBarEnable([])
  │
  ▼
Flutter 引擎启动
  │
  ▼
intent_navigation_channel.dart.init(router)
  ├─ 监听 onIntentNavigation
  ├─ _consumePending()
  │   → 拿到 { pageId, dishId }
  │   → _navigate() → 路由跳转
  │
  ▼
用户进入目标页面

七、"谁接、谁转、谁消费"的分工图

EntryAbility(接)
  ├─ 接住 Want 参数(pageId, dishId)
  ├─ 注册所有插件
  ├─ 初始化窗口环境
  │
  ▼
IntentNavigationPlugin(转)
  ├─ 路由判断:channel 可用 → 推送
  ├─ 路由判断:channel 不可用 → 暂存 pending
  │
  ▼
intent_navigation_channel.dart(消费)
  ├─ 监听实时推送
  ├─ _consumePending() 消费暂存
  ├─ pageId → Flutter 路由映射
  │
  ▼
GoRouter(跳转)
  └─ _router.go('/search') / _router.push('/dish/$dishId')

关键原则:EntryAbility 只接和转,Flutter 路由留给 Flutter 侧消费。

关键代码位置

文件

作用

app/ohos/entry/src/main/ets/entryability/EntryAbility.ets

鸿蒙启动链核心

app/ohos/entry/src/main/ets/plugins/IntentNavigationPlugin.ets

pending 缓存 + 推送

app/lib/core/platform/intent_navigation_channel.dart

Flutter 路由消费

常见坑

  • 把插件注册逻辑分散到多个地方 — 应该集中在 configureFlutterEngine

  • 冷启动参数没有先缓存onCreate 必须调用 setPendingNavigation

  • 只处理 onCreate,不处理 onNewWant — 运行期入口会丢失

  • 只把 EntryAbility 当成启动页,不把它当成系统入口汇合点 — 它是启动链、插件链、入口链的汇合点

  • EntryAbility 里直接写死 Flutter 路由字符串 — 路由应该留给 Flutter 侧

  • 只看启动成功,不检查插件链和参数链是否真的打通 — 需要验证完整链路

  • onWindowStageCreate 没有处理异常 — 窗口初始化失败会导致显示异常

可复用模板

EntryAbility 生命周期模板

export default class EntryAbility extends FlutterAbility {
  configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    // 手动注册自定义插件
    flutterEngine.getPlugins()?.add(new YourPlugin())
  }

  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    super.onCreate(want, launchParam)
    // 承接冷启动参数
    const pageId = want.parameters?.['pageId'] as string;
    if (pageId) {
      YourPlugin.setPendingNavigation(pageId);
    }
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    super.onNewWant(want, launchParam)
    // 承接运行期入口
    const pageId = want.parameters?.['pageId'] as string;
    if (pageId) {
      const plugin = YourPlugin.getInstance();
      if (plugin) {
        plugin.navigateToPage(pageId);
      } else {
        YourPlugin.setPendingNavigation(pageId);
      }
    }
  }

  onWindowStageCreate(windowStage: window.WindowStage): void {
    super.onWindowStageCreate(windowStage)
    // 初始化窗口环境
    windowStage.getMainWindow().then((mainWindow) => {
      mainWindow.setWindowLayoutFullScreen(true);
      mainWindow.setWindowSystemBarEnable([]);
    });
  }
}

启动链检查清单

EntryAbility 检查:
  □ configureFlutterEngine 是否注册了所有插件?
  □ onCreate 是否读取了 want 参数?
  □ onCreate 是否调用了 setPendingNavigation?
  □ onNewWant 是否处理了运行期入口?
  □ onNewWant 是否有 plugin null 的 fallback?
  □ onWindowStageCreate 是否初始化了窗口?
  □ 所有异常是否都 catch 了?

分工模板

EntryAbility(接 + 转)
  ├─ 注册插件
  ├─ 接住 Want 参数
  ├─ 暂存 pending
  └─ 初始化窗口

IntentNavigationPlugin(桥接)
  ├─ 判断 channel 是否可用
  ├─ 可用 → 推送
  └─ 不可用 → 暂存

Flutter 侧(消费)
  ├─ init() 监听实时推送
  ├─ _consumePending() 消费暂存
  └─ pageId → 路由跳转

本篇总结

EntryAbility 在鸿蒙 Flutter 项目里是启动链、插件链和入口链的汇合点。它同时承担 4 个职责:

  1. 配置 Flutter 引擎configureFlutterEngine 注册所有插件

  2. 承接冷启动参数onCreate 读取 want 并暂存 pending

  3. 承接运行期入口onNewWant 尝试推送或暂存

  4. 初始化窗口环境onWindowStageCreate 设置全屏和状态栏

先把这一个文件看懂,后面的插件注册和系统直达就会清楚很多。对多入口项目来说,它绝不是一个可以忽略的外围文件。

Logo

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

更多推荐