news 2026/9/10 11:35:48

egui Android 开发入门:从环境搭建到 hello_android 示例的完整构建运行指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
egui Android 开发入门:从环境搭建到 hello_android 示例的完整构建运行指南

egui Android 开发入门:从环境搭建到 hello_android 示例的完整构建运行指南

【免费下载链接】eguiegui: an easy-to-use immediate mode GUI in Rust that runs on both web and native项目地址: https://gitcode.com/GitHub_Trending/eg/egui

导读

本文以 egui 仓库中的官方 hello_android 示例 为骨架,完整讲解如何把基于 eframe 的 Rust 即时模式 GUI 应用编译成 Android APK 并在真机/模拟器上运行。你将掌握 Android 交叉编译工具链的搭建、环境变量的配置、cargo-apk的安装与使用,以及桌面端与 Android 端共用同一套代码的工程结构,读完即可把现有 egui 应用移植到 Android 平台。

示例概览:一个应用,两个平台

hello_android是 eframe 官方提供的 Android 最小可运行示例。它的独特之处在于:同一个 crate 同时面向桌面(native)与 Android(移动端)两个目标平台编译,而应用逻辑本身完全复用。

从 examples/hello_android/Cargo.toml 可以看到其关键设计:

[lib] # cdylib is required for Android, lib is required for desktop crate-type = ["cdylib", "lib"]
  • cdylib:Android 需要以动态库(.so)形式打包进 APK,由系统 Java 层通过 JNI 加载;
  • lib:桌面端(Linux/macOS/Windows)以常规 Rust 库形式链接进可执行文件。

两者共存,保证了cargo apk run(Android)与cargo run(桌面)都能直接使用同一个工程。

平台相关的两个入口

示例的源码分为两个文件:

  • examples/hello_android/src/main.rs:桌面入口,调用eframe::run_native创建原生窗口;
  • examples/hello_android/src/lib.rs:同时包含桌面入口与 Android 入口,其中 Android 入口通过#[cfg(target_os = "android")]条件编译隔离:
#[cfg(target_os = "android")] #[unsafe(no_mangle)] fn android_main(app: winit::platform::android::activity::AndroidApp) { // Log to android output android_logger::init_once( android_logger::Config::default().with_max_level(log::LevelFilter::Info), ); let options = eframe::NativeOptions { android_app: Some(app), ..Default::default() }; eframe::run_native( "My egui App", options, Box::new(|cc| Ok(Box::new(MyApp::new(cc)))), ) .unwrap() }

要点解析:

  • android_mainandroid-activity(通过 winit 暴露)约定的入口函数,必须#[unsafe(no_mangle)]导出符号,供 Android 原生层调用;
  • 通过android_logger把 Rust 的log输出桥接到 Androidlogcat,级别设为Info,便于调试;
  • NativeOptions中的android_app字段(见 crates/eframe/src/epi.rs 中#[cfg(target_os = "android")]的声明)是 Android 平台下eframe::run_native必需的运行时上下文,缺失会直接报错。

Android 特性开关

hello_android在依赖声明中启用了两个关键特性(见 examples/hello_android/Cargo.toml):

eframe = { workspace = true, default-features = false, features = [ "default_fonts", "glow", "android-native-activity", ] }
  • glow:选用 OpenGL ES 渲染后端(Android 移动 GPU 兼容性最好);
  • android-native-activity:选择android-activitynative-activity后端。

这两个特性从eframe一路透传到egui-winitwinit

  • crates/eframe/Cargo.toml 中android-native-activity = ["egui-winit/android-native-activity"]
  • crates/egui-winit/Cargo.toml 中android-native-activity = ["winit/android-native-activity"]

eframe还提供另一个备选后端android-game-activity(对应egui-winit/android-game-activity)。从 crates/eframe/src/lib.rs 的编译期约束可以看出,若同时开启accesskit辅助功能特性,则必须使用android-game-activity后端,否则编译会直接失败(compile_error!)。因此选型时需要注意:需要无障碍支持的应用应选择android-game-activity

桌面端前置条件:交叉编译工具链

Android 应用需要 Rust 交叉编译目标与 Android SDK/NDK。以下按官方 README 的顺序展开,并补充必要说明。

1. 添加 Rust Android 编译目标

rustup target add armv7-linux-androideabi aarch64-linux-android
  • aarch64-linux-android:64 位 ARM 目标,覆盖当今绝大多数主流手机(arm64-v8a);
  • armv7-linux-androideabi:32 位 ARM 目标,覆盖较老的 32 位设备(armeabi-v7a)。

这两个目标与 examples/hello_android/Cargo.toml 中[package.metadata.android]声明的build_targets一一对应:

[package.metadata.android] build_targets = ["armv7-linux-androideabi", "aarch64-linux-android"]

若机器上只有 64 位设备,也可以只添加aarch64-linux-android并相应调整build_targets,以缩短构建时间。

2. 设置环境变量(每次构建前必须执行)

export ANDROID_HOME="$HOME/tools/android" export ANDROID_NDK_ROOT="${ANDROID_HOME}/ndk/29.0.14206865" export PATH="$PATH:${ANDROID_NDK_ROOT}:${ANDROID_HOME}/build-tools/${BUILDTOOLS_VERSION}:${ANDROID_HOME}/cmdline-tools/bin"

官方特别强调:这些变量是cargo apk每次运行都需要的。其中:

  • ANDROID_HOME:Android SDK 根目录,本例为$HOME/tools/android
  • ANDROID_NDK_ROOT:指向具体版本的 NDK 目录(ndk/29.0.14206865);
  • PATH:追加 NDK、SDK build-tools(版本号用${BUILDTOOLS_VERSION}占位,与第 4 步安装的版本保持一致)以及 cmdline-tools 的bin目录。

建议把这些export写入~/.bashrc~/.profile,避免每次开新终端重复配置。若你安装了 Android Studio,其 SDK 默认位于$HOME/Android/Sdk,将ANDROID_HOME指向该目录同样可行,关键是 SDK、NDK、build-tools 版本要与下面的安装步骤一致。

3. 安装 Android 命令行工具

mkdir -p "${ANDROID_HOME}/cmdline-tools" curl -sLo /tmp/clt.zip https://dl.google.com/android/repository/commandlinetools-linux-14742923_latest.zip unzip -d "${ANDROID_HOME}" /tmp/clt.zip

将 Google 官方的 commandline-tools 压缩包下载并解压到ANDROID_HOME,随后即可使用其中的sdkmanager安装 SDK 组件。cmdline-toolsbin目录已在第 2 步加入PATH

4. 安装 SDK 组件

sdkmanager --sdk_root="${ANDROID_HOME}" --install "build-tools;36.0.0" "ndk;29.0.14206865" "platforms;android-35"

安装内容:

  • build-tools;36.0.0:打包 APK 所需的构建工具(aapt2、zipalign 等),对应PATH中的${BUILDTOOLS_VERSION}
  • ndk;29.0.14206865:NDK 29,提供交叉编译所需的 C 工具链与头文件,目录结构为$ANDROID_HOME/ndk/29.0.14206865,与ANDROID_NDK_ROOT一致;
  • platforms;android-35:Android 35(Android 15)平台库。

注意:官方在文档中特别提示“You may need to change SDK versions”——以上版本号是编写该示例时的推荐组合,请根据你本机可用的 SDK/NDK 版本与目标设备系统版本灵活调整,并保持ANDROID_NDK_ROOTPATH中的 build-tools 版本、sdkmanager安装版本三者一致。

SDK 版本信息在工程中也有一处对应:Cargo.toml[package.metadata.android.sdk]声明了min_sdk_version = 23(最低支持 Android 6.0)、target_sdk_version = 35(目标 Android 15)。

5. 安装 cargo-apk 构建工具

cargo install --git https://github.com/parasyte/cargo-apk.git --rev 282639508eeed7d73f2e1eaeea042da2716436d5 cargo-apk

cargo-apkcargo的子命令扩展,负责把 Rust crate 打包为可安装的 APK。官方 README 特别注明:上游存在一个 bug(对应 issue 链接见 examples/hello_android/README.md),因此必须安装指定--rev(提交哈希282639508eeed7d73f2e1eaeea042da2716436d5)的修复版本,直接cargo install cargo-apk装到最新版可能无法正常工作。

安装完成后可通过cargo apk --help验证是否成功注册为 cargo 子命令。

构建与运行:一行命令双平台

官方 README 给出的两条命令:

# Android:交叉编译并打包安装到连接的设备/模拟器 cargo apk run -p hello_android --lib # 桌面:直接作为原生应用运行 cargo run -p hello_android
  • cargo apk run -p hello_android --lib-p指定包名;--lib告诉 cargo-apk 打包库目标(即上文的cdylib),编译产物.so会被封装进 APK,安装后由 Android 系统拉起。运行前提是已有设备通过 adb 连接(adb devices可确认),首次运行也可加上--release获得优化后的性能;
  • cargo run -p hello_android:走的是 examples/hello_android/src/main.rs 的桌面入口,与普通 eframe 应用无异,可用于在开发 Android 功能前快速验证 UI 逻辑,无需等待交叉编译。

应用代码解读

MyApp是桌面与 Android 共享的应用主体(examples/hello_android/src/lib.rs):

pub struct MyApp { demo: egui_demo_lib::DemoWindows, } impl MyApp { pub fn new(cc: &CreationContext) -> Self { egui_extras::install_image_loaders(&cc.egui_ctx); Self { demo: egui_demo_lib::DemoWindows::default(), } } } impl eframe::App for MyApp { fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) { // Reserve some space at the top so the demo ui isn't hidden behind the android status bar egui::Panel::top("status_bar_space").show(ui, |ui| { ui.set_height(32.0); }); egui::CentralPanel::default().show(ui, |ui| { self.demo.ui(ui); }); } }

三个值得注意的细节:

  1. 复用官方演示集egui_demo_lib::DemoWindows是仓库自带的演示窗口集合,让示例开箱即用地展示大量控件;
  2. 图片加载egui_extras::install_image_loaders注册图片加载器(依赖中启用了egui_extrasimage特性),为演示里的图片控件提供支持;
  3. 状态栏避让Panel::top("status_bar_space")在顶部预留 32 像素高度,避免 UI 被 Android 系统状态栏遮挡。源码注释同时指出这是一个临时 hack,待 winit 在 Android 上实现 safe_area 后应替换为正式方案——如果你要移植自己的应用,这个处理思路可以直接复用。

Android 渲染生命周期(可选:深入底层)

对想了解底层机制的读者,eframe 的 glow 集成在 Android 上有一个特殊生命周期处理,见 crates/eframe/src/native/glow_integration.rs:

  • Android 应用会收到Resumed/Suspended事件,与桌面平台不同(桌面只在启动时进入一次);
  • 在 Android 上,Suspended时会销毁 GL surface 与窗口并让 OpenGL 上下文失活Resumed时则重新创建(glow 集成注释明确写道:Suspended: on android, we drop window + surface);
  • 这也是为什么示例在桌面与 Android 上都要走eframe::run_native——eframe 已经帮你封装好了这套平台差异。

入口侧,eframe在 crates/eframe/src/native/run.rs 中通过winit::platform::android::EventLoopBuilderExtAndroidwith_android_appNativeOptions.android_app注入事件循环,从而衔接android_main与 winit 事件循环。

常见问题与排查建议

  • cargo apk报找不到 SDK/NDK:确认ANDROID_HOMEANDROID_NDK_ROOT已 export,且ANDROID_NDK_ROOT指向真实存在的 NDK 版本目录;版本不匹配时参考官方提示调整 SDK 版本。
  • 构建目标缺失rustup target add的目标必须与build_targets一致,缺哪个补哪个。
  • UI 被状态栏遮挡:参考示例的顶部Panel预留方案,或等待 winit safe_area 支持落地后改用正式 API。
  • android_main未找到 / 链接失败:确认eframe启用了android-native-activity(或android-game-activity)特性,且入口函数保持了#[unsafe(no_mangle)]符号导出。
  • 启用accesskit后编译失败:按 crates/eframe/src/lib.rs 中的compile_error!提示,将后端切换到android-game-activity

相关资源

  • hello_android 示例文档:本文的原始依据;
  • hello_android 工程配置:特性开关、build_targets、SDK 版本声明;
  • hello_android 共享应用代码:双平台入口与MyApp实现;
  • eframe Android 入口与事件循环集成:android_app注入 winit 事件循环的实现;
  • eframe glow 集成中的 Android 生命周期处理:Resumed/Suspended时 surface 与窗口的创建销毁逻辑;
  • eframe NativeOptions 的 android_app 字段:Android 平台特有配置项;
  • eframe 特性声明:android-native-activity/android-game-activity两个后端的特性透传。

【免费下载链接】eguiegui: an easy-to-use immediate mode GUI in Rust that runs on both web and native项目地址: https://gitcode.com/GitHub_Trending/eg/egui

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 11:34:51

虚拟惯量环节全解析:从转子运动方程到并网工程实现

做风电场并网测试这些年,我越来越明显地感觉到一个变化:电网对新能源场站的频率响应能力要求,已经从“能调压”升级到了“能调频”,甚至到了“你把转子运动特性给我补出来”的程度。这里的核心就是虚拟惯量环节。很多刚接触这个概…

作者头像 李华
网站建设 2026/9/10 11:34:32

TVBoxOSC 使用指南:把电视盒子变成全能播放中心

TVBoxOSC 使用指南:把电视盒子变成全能播放中心 【免费下载链接】TVBoxOSC TVBoxOSC - 一个基于第三方项目的代码库,用于电视盒子的控制和管理。 项目地址: https://gitcode.com/GitHub_Trending/tv/TVBoxOSC TVBoxOSC 是一个面向电视盒子的开源播…

作者头像 李华
网站建设 2026/9/10 11:32:01

Flutter+鸿蒙跨平台开发实战与优化

1. 项目概述:Flutter鸿蒙的跨平台开发实践去年接手一个电商促销工具开发需求时,我首次尝试用Flutter框架为鸿蒙系统开发购物满减计算器。这个看似简单的需求背后,涉及到Flutter在鸿蒙平台的兼容性适配、跨平台状态管理、以及复杂促销规则引擎…

作者头像 李华