Flutter/Go 多平台构建与调试工具链梳理

December 15, 2025
4 min read
By devshan

Table of Contents

This is a list of all the sections in this post. Click on any of them to jump to that section.

一、目标与整体思路

  • 目标:为 Android 和 Windows 两端建立一套稳定、可重复的“构建 + 运行 + 调试”工具链,减少手工步骤,避免把调试兼容逻辑写死在业务代码里。
  • 约束:
    • 客户端代码尽量只承载业务逻辑和必要的跨平台抽象,不为了解决构建/调试问题去做“路径探测”等脏活。
    • 所有与平台路径、可执行文件位置相关的逻辑尽量收敛到脚本层。
  • 当前结果:
    • Android:有 build_android.batrun_android.bat 分别负责“产物构建”和“真机调试运行”,自动生成 gomobile AAR 并跑起 Flutter。
    • Windows:有 build_windows.batrun_windows.bat 分别负责 Release 构建与 Debug 调试,自动准备内嵌核心 exe 并跑起 Flutter 桌面端。

二、Android 侧:build / run 脚本与环境修复

1. build_android.bat:构建 AAR 与 APK

  • 路径:scripts/bat/build_android.bat
  • 主要职责:
    • 设置 Android 构建相关环境变量:
      • ANDROID_HOME 指向本机 SDK。
      • ANDROID_NDK_HOME 指向 NDK。
      • PATH 注入 Android Studio 自带的 JBR,避免本机 JDK 版本不一致。
    • core 目录下执行 gomobile bind
      • 目标:生成 e2eepan-mobile.aar 并输出到 client/android/app/libs
    • client 目录下执行 flutter build apk
      • 产出标准 Release APK。
  • 遇到的典型问题:gomobile 依赖缺失。
    • 报错内容集中在找不到 golang.org/x/mobile/bind
    • 根因是 Go 环境中没有拉取对应模块,且没有在有 go.mod 的模块目录下做一次 go get
    • 修复方案:
      • 在任意目录安装 gomobile 命令并初始化:
        • go install golang.org/x/mobile/cmd/gomobile@latest
        • gomobile init
      • core 模块下拉取 bind 依赖,使之写入 core/go.mod
        • cd core
        • go get golang.org/x/mobile/bind@latest
      • 再执行 build_android.batgomobile bindflutter build apk 即可顺利完成。

2. run_android.bat:一键真机调试

  • 路径:scripts/bat/run_android.bat
  • 主要职责:
    • 与 build 脚本一样,在 core 下先执行一次 gomobile bind
      • 确保 AAR 是最新的。
    • 然后进入 client,执行 flutter run
      • 在连接的 Android 设备上直接运行 debug 版客户端。
  • 效果验证:
    • 运行脚本后,logcat 中可以看到 Go 内核通过 gomobile 启动的日志,类似:
      • GoLog: mobile core http server starting on http://127.0.0.1:<port>
    • 同时可以看到大量 MIUI 输入事件日志,证明 Flutter 客户端正常响应操作。
  • 经验:
    • 将 gomobile AAR 生成放在 run 脚本前半段,可以保证每次真机调试时,native 核心和客户端 Dart 代码始终是配套版本。
    • Android 端所有内嵌核心启动逻辑依旧仅存在于 Kotlin + gomobile 层,Flutter 只是发 MethodChannel 请求,工具链细节全部藏在脚本里。

三、Windows 侧:Debug 与 Release 的脚本分工

1. run_windows.bat:Debug 模式调试 + 自动内核

  • 路径:scripts/bat/run_windows.bat
  • 目标:在 Windows 上体验“和 Android 类似的内嵌模式”,即:
    • flutter run -d windows 启动调试版客户端。
    • 客户端在桌面端同样自动拉起 Go 核心进程。
  • 最终实现方式:
    • core 目录下:
      • 预先设置核心输出目录:
        • ..\client\build\windows\x64\runner\Debug
      • 若目录不存在,先 mkdir
      • 直接将 Go 核心编译到该目录下:
        • go build -o "<Debug runner 目录>\e2eepan-core.exe" ./cmd/server
    • 然后进入 client,简单执行:
      • flutter run -d windows
  • 关键设计点:
    • 客户端代码始终只按照“自己 exe 同目录下有 e2eepan-core.exe”这个约定工作,不再有任何“向上爬目录找 exe”之类的调试兼容逻辑。
    • Debug 模式下 exe 的实际路径由 Flutter 和 CMake 决定,脚本只负责在那个目录里准备好核心 exe。
  • 踩过的坑:
    • 第一版实现是客户端在运行时向上爬多级目录寻找 e2eepan-core.exe,虽然能解决问题,但把调试路径细节带进了 AppState,违反了“客户端只负责业务”的原则。
    • 后来改为:客户端恢复到只看当前目录,脚本负责把核心放到 Debug runner 目录,这样业务代码保持纯粹,平台适配集中在脚本。

2. build_windows.bat:Release 构建 + Release 内核

  • 路径:scripts/bat/build_windows.bat
  • 目标:产出一个可以直接双击运行、并能自动启动内嵌核心的 Release 包。
  • 最终实现方式:
    • core 目录下:
      • 预先设置 Release runner 目录:
        • ..\client\build\windows\x64\runner\Release
      • 若目录不存在,先 mkdir
      • 直接将 Go 核心编译到该目录:
        • go build -o "<Release runner 目录>\e2eepan-core.exe" ./cmd/server
    • 然后进入 client,执行:
      • flutter build windows
  • 关键调整:
    • 早期版本是先把核心编译到 client\e2eepan-core.exe,再用 copy 复制到 Release 目录,并且错误输出被重定向到了 nul,导致即使 copy 失败也看不到提示,最终 Release 目录缺少内核。
    • 现在改为“直接编译到 Release 目录”,整个流程更简单、更不容易出错。
  • 结果:
    • Release 目录内容包括:
      • e2eepan_client.exe
      • e2eepan-core.exe
      • 各种 Flutter 运行必需的 dll 和 data 目录。
    • 双击 e2eepan_client.exe 即可运行完整的“桌面客户端 + 内嵌 Go 核心”。

四、客户端代码与工具链的边界划分

1. 客户端的职责

  • 在 Flutter 层:
    • 维护 core_modeexternal / embedded),并在 Windows 和 Android 上默认使用内嵌模式。
    • 在内嵌模式下:
      • Android:通过 MethodChannelNativeCoreService.startEmbeddedCore
      • Windows:在桌面端直接通过 Process.start 启动 e2eepan-core.exe,路径仅假设“和当前 exe 在同一目录”。
    • 提供若干调试入口(如 Debug 页里的“重启内核 / 查看健康状态”等),帮助验证内核状态与 S3 配置。

2. 工具链脚本的职责

  • Android:
    • 负责准备 gomobile AAR,保证 Android 端的内嵌核心二进制与 Go 源码版本一致。
    • 在 run 脚本中同时处理 AAR 更新和 Flutter 启动逻辑,保证“一条命令即可真机调试”。
  • Windows:
    • 负责在 Debug/Release 对应目录中准备好 e2eepan-core.exe
    • 通过 run_windows.batbuild_windows.bat 分别承担“调试体验”和“正式发行”的职责。
  • 共识:
    • 所有路径、环境变量、外部工具(gomobile、NuGet 等)的坑,尽量封装在脚本内解决。
    • 客户端只依赖少量稳定的约定(例如“内核 exe 在我旁边”),不掺杂临时调试兼容逻辑。

五、常见问题与经验小结

  1. gomobile 相关错误:
    • 如果 gomobile bindunable to import bind: no Go package in golang.org/x/mobile/bind
      • 检查是否在包含 go.mod 的模块目录下执行过 go get golang.org/x/mobile/bind@latest
      • 确保已经执行过一次 gomobile init
  2. PowerShell 语法坑:
    • 不要在脚本里混用 &&,在当前 PowerShell 环境下会被解析错误。
    • 统一使用一条命令 + 显式工作目录,或者用批处理脚本组合多条命令。
  3. Flutter Windows 构建依赖 NuGet:
    • 第一次运行 flutter build windowsflutter run -d windows 时,NuGet 可能会被自动下载。
    • 日志中的 “Nuget.exe not found, trying to download or use cached version” 不是错误,只是提示。
  4. 产物与源码分离:
    • client/.gitignore 中忽略了所有 *.exe
      • 避免将 Windows 可执行文件误提交到仓库。
      • 调试与构建产物只存在于本地 build 目录和脚本指定的位置。

六、后续可以考虑的优化

  1. 为这四个脚本增加一个简单的顶层 README 或“脚本使用说明”,帮助新同事快速理解 build/run 流程。
  2. 在脚本中加入更友好的错误提示(例如明确打印“gomobile 未初始化,请先运行 gomobile init”),进一步降低环境问题的排查成本。
  3. 为 Windows/Android 的构建增加 CI 任务,定期验证脚本在干净环境下是否仍能顺利运行。