16 个不稳定版本 (5 个破坏性更新)
0.6.0 | 2024 年 4 月 26 日 |
---|---|
0.5.2 | 2024 年 1 月 30 日 |
0.5.1 | 2023 年 12 月 20 日 |
0.5.0 | 2023 年 10 月 16 日 |
0.1.1 | 2022 年 7 月 4 日 |
#20 在 GUI
194,721 每月下载量
用于 1,481 个 crate (8 直接)
1.5MB
42K SLoC
android-activity
概述
android-activity
提供了一个构建 Android 上的原生 Rust 应用程序的 "粘合层",支持多个 Activity
基类。它与 C/C++ 应用程序的 android_native_app_glue.c
类似,是 ndk-glue crate 的替代品。
android-activity
通过 Android Activity
类的 onCreate
方法将您的 crate 作为 cdylib
库加载;在 Java 主线程之外运行 android_main()
函数,并在 Java 和您的本地线程之间传递事件(如生命周期事件和输入事件)。
目前它支持 NativeActivity
或 GameActivity
(来自 Android 游戏开发套件),并且还支持一个面向 Rust 应用程序需求的第一方 RustActivity
基类。
示例
Cargo.toml
[dependencies]
log = "0.4"
android_logger = "0.13"
android-activity = { version = "0.5", features = [ "native-activity" ] }
[lib]
crate_type = ["cdylib"]
注意:您需要指定 "native-activity" 功能或 "game-activity" 功能以标识您的应用程序基于哪个 Activity
基类
lib.rs
use android_activity::{AndroidApp, InputStatus, MainEvent, PollEvent};
#[no_mangle]
fn android_main(app: AndroidApp) {
android_logger::init_once(android_logger::Config::default().with_min_level(log::Level::Info));
loop {
app.poll_events(Some(std::time::Duration::from_millis(500)) /* timeout */, |event| {
match event {
PollEvent::Wake => { log::info!("Early wake up"); },
PollEvent::Timeout => { log::info!("Hello, World!"); },
PollEvent::Main(main_event) => {
log::info!("Main event: {:?}", main_event);
match main_event {
MainEvent::Destroy => { return; }
_ => {}
}
},
_ => {}
}
app.input_events(|event| {
log::info!("Input Event: {event:?}");
InputStatus::Unhandled
});
});
}
}
rustup target add aarch64-linux-android
cargo install cargo-apk
cargo apk run
adb logcat example:V *:S
完整示例
请参阅 这个示例集合(基于 GameActivity
和 NativeActivity
)。
每个示例都是一个独立的项目,也可以用作启动新项目的方便模板。
基于中间件框架(如Winit和Egui)的示例旨在演示如何编写可在Android和其他系统上运行的便携式代码。
我应该使用NativeActivity还是GameActivity?
要了解更多关于随Android提供的NativeActivity
类的信息,请参阅这里。
要了解更多关于Android Game Developer's Kit中的GameActivity
类以及与NativeActivity
的比较,请参阅这里
一般来说,如果您不确定,由于可能不需要编译/链接任何Java或Kotlin代码,NativeActivity
可能是一个更好的起点。
预计GameActivity
的后端将随着时间的推移获得更复杂的输入处理功能(例如,支持通过屏幕键盘或游戏控制器进行输入),并且只有GameActivity
是基于您可能希望在某些情况下帮助实现设备兼容性的AppCompatActivity
子类。
即使您最初出于方便使用NativeActivity
,大多数中等复杂的应用程序最终可能需要定义自己的Activity
子类(无论是从NativeActivity
或GameActivity
继承),这将需要编译至少一小段Java或Kotlin代码。这通常是由于Android的设计导致的,它通过Activity
类引导大量事件,这些事件只能通过重载一些相关Activity方法来处理。
从ndk-glue切换到android-activity
基于Winit的应用程序
首先;如果您有一个基于Winit的应用程序,并且明确依赖于ndk-glue
,则您的应用程序需要在Winit 0.28版本中删除对ndk-glue
的依赖,因为Winit 0.28版本将基于android-activity(由于glue crates的性质,它们不能与替代glue crates兼容)。
基于Winit的应用程序可以遵循Android README中的指导来获取如何切换的建议。大多数基于Winit的应用程序应努力删除对特定glue crate的任何明确依赖(因此不是直接依赖于ndk-glue
或android-activity
,而是依赖于Winit来拉取正确的glue crate)。然后,主要实际变化将是添加一个#[no_mangle]android_main(app: AndroidApp)
入口点。
有关更多详细信息,请参阅Android README,并请参阅这里的基于Winit的示例。
中间件crate(即不是应用程序)
如果您有一个被认为是中间件库的crate(例如,使用JNI来支持对蓝牙或Android的相机API的访问),则该crate几乎肯定应该删除对特定glue crate的任何依赖,因为这会强制实施严格的兼容性约束,这意味着crate只能由使用该特定glue crate版本的应用程序使用。
中间件库可以改用 ndk-context 包作为使用 JNI 而不必对应用程序的整体架构做出任何假设的手段。这样,中间件包就可以与替代粘合包(包括 ndk-glue
和 android-activity
)一起工作,以及与嵌入式用例(即可能仅嵌入由 Rust 编写的动态库以实现某些原生函数的非原生应用程序)一起工作。
其他非 Winit 基础的应用程序
将简单独立应用程序从 ndk-glue
切换到 android-activity
(仍然基于 NativeActivity
)的步骤应该是
- 从你的 Cargo.toml 中移除
ndk-glue
- 添加对
android-activity
的依赖,例如android-activity = { version="0.5", features = [ "native-activity" ] }
- 可选地添加对
android_logger = "0.13.0"
- 更新
main
入口点,如下所示
use android_activity::AndroidApp;
#[no_mangle]
fn android_main(app: AndroidApp) {
android_logger::init_once(android_logger::Config::default().with_min_level(log::Level::Info));
}
有关如何轮询事件的更多详细信息,请参阅此最小化的 NativeActivity 主循环
由于需要传递 AndroidApp
参数,该参数与传统 main()
函数不兼容,因此没有 #[ndk_glue::main]
替代方案。具有 Android 特定入口点还可以提供初始化 Android 日志和处理其他 Android 特定细节(如根据 app
参数构建事件循环)的位置。
设计摘要 / android-activity 的动机
在着手开发 android-activity 之前,发现可用于在 Android 上构建独立 Rust 应用程序的现有粘合包存在一些技术限制,这些限制是该包旨在解决的。
- 支持替代 Activity 类:以前的粘合包基于
NativeActivity
,它们的 API 不支持替代方案。特别是,对与GameActivity
类及其GameTextInput
库结合使用的兴趣,这可以方便地支持屏幕键盘。这也允许基于标准的AppCompatActivity
基础类构建应用程序,这是使用NativeActivity
所不可能的。最后,还感兴趣于为支持第一方RustActivity
开辟道路,该活动可以根据 Android 上 Rust 应用程序的需求进行最佳调整。 - 封装 IPC 和原生线程与 JVM 线程之间的同步:例如,使用
ndk-glue
,应用程序本身需要通过遵循锁定约定来避免原生线程和 Java 线程之间的竞态条件,并且对于也需要同步的其他请求(如状态保存)支持尚不明确。 - 避免静态全局状态:考虑到支持具有多个原生活动的应用程序的可能性,人们对一个不依赖于全局静态来跟踪顶级状态的 API 感兴趣。不是有全局状态获取器,而是
android-activity
将一个显式的app: AndroidApp
参数传递给封装单个Activity
状态的入口点。
MSRV
我们的目标是(至少)支持过去三个月内发布的 Rust 的稳定版本。Rust 的发布周期为 6 周,这意味着我们将支持过去三个稳定版本。例如,当 Rust 1.69 发布时,我们将限制我们的 rust-version
为 1.67。
我们只会在我们依赖新功能或依赖项提高了其 MSRV 时提升 rust-version
,并且不会过于贪婪。换句话说,我们只会将 MSRV 设置为所需的最低版本。
MSRV 更新不被视为固有的 semver 破坏(除非在公共 API 中公开了新功能),因此 rust-version
的更改可能在补丁版本中发生。
依赖项
~5–16MB
~202K SLoC