Rust NDK宏库ndk-macro的使用,简化Android NDK开发的Rust宏工具

Rust NDK宏库ndk-macro的使用,简化Android NDK开发的Rust宏工具

ndk-macro是实现属性过程宏main的实现,该宏直接应用于主函数。

此宏在ndk-glue中重新导出。通常,不需要直接依赖此库!

用法

#[cfg_attr(target_os = "android", ndk_glue::main(backtrace = "on"))]
pub fn main() {
    println!("hello world");
}

属性宏支持可选的输入属性:

  • backtrace = "on|full":通过设置RUST_BACKTRACE环境变量启用回溯
  • ndk_glue = "path::to::ndk_glue":覆盖默认路径到ndk_glue crate
  • logger(...props):使用传递的配置配置android记录器(需要logger功能):
    • level = "error|warn|info|debug|trace":更改记录器的日志级别
    • tag = "my-tag":为记录器分配标签
    • filter = "filtering-rules":更改默认过滤规则

完整示例demo

// Cargo.toml依赖配置
// [dependencies]
// ndk-glue = "0.3.0"

// 主函数使用ndk_glue::main宏
#[cfg_attr(target_os = "android", ndk_glue::main(
    backtrace = "on",           // 启用回溯功能
    logger(                     // 配置日志记录器
        level = "info",         // 设置日志级别为info
        tag = "rust-app"        // 设置日志标签
    )
))]
pub fn main() {
    // Android平台上的入口点
    println!("Hello from Rust on Android!");
    
    // 这里可以添加你的Android NDK逻辑
    // 例如调用JNI接口、处理Android事件等
}
// 另一个使用自定义ndk_glue路径的示例
#[cfg_attr(target_os = "android", ndk_glue::main(
    backtrace = "full",         // 启用完整回溯
    ndk_glue = "my_custom_path::to::ndk_glue", // 自定义ndk_glue路径
    logger(
        level = "debug",        // 调试级别日志
        tag = "custom-app",     // 自定义标签
        filter = "my_app=debug" // 自定义过滤规则
    )
))]
pub fn main() {
    println!("Custom Android NDK application");
    // 应用程序逻辑
}

1 回复

Rust NDK宏库ndk-macro:简化Android NDK开发的Rust宏工具

介绍

ndk-macro是一个专为简化Rust与Android NDK交互而设计的宏库。它通过提供一系列过程宏,帮助开发者更轻松地创建JNI接口、处理类型转换和管理Android生命周期,显著减少模板代码量。

主要功能

  • 自动生成JNI绑定代码
  • 简化Rust与Java/Kotlin的类型转换
  • 提供Android生命周期管理宏
  • 支持异步任务处理

安装方法

在Cargo.toml中添加依赖:

[dependencies]
ndk-macro = "0.3"
ndk = "0.7"

使用示例

1. 基本JNI函数声明

use ndk_macro::jni_fn;

#[jni_fn("com.example.rustlib.RustBridge")]
pub extern "C" fn add_numbers(env: JNIEnv, obj: JObject, a: i32, b: i32) -> i32 {
    a + b
}

2. 自动类型转换

use ndk_macro::java_string;

#[jni_fn("com.example.rustlib.StringUtils")]
pub extern "C" fn concatenate_strings(
    env: JNIEnv,
    obj: JObject,
    str1: JString,
    str2: JString
) -> JString {
    let s1: String = java_string!(env, str1);
    let s2: String = java_string!(env, str2);
    
    let result = format!("{}{}", s1, s2);
    env.new_string(result).unwrap()
}

3. 异步任务处理

use ndk_macro::async_jni;

#[async_jni("com.example.rustlib.AsyncOperations")]
pub async fn fetch_data_async(url: String) -> Result<String, String> {
    // 异步网络请求实现
    Ok("Data fetched successfully".to_string())
}

4. 生命周期管理

use ndk_macro::android_lifecycle;

#[android_lifecycle]
pub struct DataProcessor {
    cache: HashMap<String, String>,
}

impl DataProcessor {
    pub fn new() -> Self {
        Self {
            cache: HashMap::new(),
        }
    }
    
    pub fn process_data(&mut self, key: String, value: String) {
        self.cache.insert(key, value);
    }
}

完整示例demo

// 导入必要的库和宏
use ndk_macro::{jni_fn, java_string, async_jni, android_lifecycle};
use jni::JNIEnv;
use jni::objects::{JObject, JString};
use std::collections::HashMap;

// 示例1: 基本数学运算函数
#[jni_fn("com.example.rustlib.MathUtils")]
pub extern "C" fn multiply_numbers(
    env: JNIEnv, 
    obj: JObject, 
    a: i32, 
    b: i32
) -> i32 {
    // 简单的乘法运算
    a * b
}

// 示例2: 字符串处理函数
#[jni_fn("com.example.rustlib.StringUtils")]
pub extern "C" fn to_uppercase(
    env: JNIEnv,
    obj: JObject,
    input: JString
) -> JString {
    // 使用java_string!宏转换JString到Rust String
    let rust_str: String = java_string!(env, input);
    
    // 转换为大写
    let result = rust_str.to_uppercase();
    
    // 创建新的Java字符串并返回
    env.new_string(result).unwrap()
}

// 示例3: 异步数据处理函数
#[async_jni("com.example.rustlib.AsyncProcessor")]
pub async fn process_data_async(
    data: String
) -> Result<String, String> {
    // 模拟异步处理
    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
    
    // 返回处理结果
    Ok(format!("Processed: {}", data))
}

// 示例4: 生命周期管理结构体
#[android_lifecycle]
pub struct DataManager {
    data_store: HashMap<String, Vec<String>>,
}

impl DataManager {
    pub fn new() -> Self {
        Self {
            data_store: HashMap::new(),
        }
    }
    
    // 添加数据到管理器
    pub fn add_data(&mut self, key: String, value: String) {
        self.data_store
            .entry(key)
            .or_insert_with(Vec::new)
            .push(value);
    }
    
    // 获取数据
    pub fn get_data(&self, key: &str) -> Option<&Vec<String>> {
        self.data_store.get(key)
    }
}

// 主模块测试
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_multiply_numbers() {
        // 测试数学函数
        assert_eq!(multiply_numbers(2, 3), 6);
    }

    #[test]
    fn test_data_manager() {
        // 测试数据管理器
        let mut manager = DataManager::new();
        manager.add_data("test".to_string(), "value".to_string());
        
        assert!(manager.get_data("test").is_some());
    }
}

配置说明

在android模块的build.gradle中添加:

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                arguments "-DANDROID_STL=c++_shared"
            }
        }
    }
}

最佳实践

  1. 使用#[jni_fn]宏标注所有需要暴露给Java/Kotlin的函数
  2. 利用java_string!宏处理字符串类型转换
  3. 对于耗时操作,使用#[async_jni]实现异步接口
  4. 通过#[android_lifecycle]管理有状态组件的生命周期

注意事项

  • 确保Android NDK版本与ndk-macro兼容
  • 在Cargo.toml中正确配置ndk版本
  • 处理JNI异常时使用提供的错误处理宏

这个宏库大大简化了Rust在Android平台上的集成工作,让开发者能够更专注于业务逻辑的实现。

回到顶部