6 个版本 (破坏性更新)
0.6.0 | 2022年11月12日 |
---|---|
0.5.0 | 2022年3月30日 |
0.4.0 | 2022年3月18日 |
0.3.0 | 2022年3月13日 |
0.1.0 | 2022年3月8日 |
#306 在 过程宏
每月下载量 24 次
600KB
9K SLoC
包含 (JAR 文件,60KB) gradle-wrapper.jar,(JAR 文件,55KB) gradle-wrapper.jar
Rsbind
Rsbind 提供工具将 Rust 特性绑定到其他语言并直接导出库工件。它还帮助您像在本地语言中编写的那样调用 Rust 服务。
它从 Rust 包生成绑定并将其打包为 Android aar、iOS lib 或其他库工件。您不需要使用此工具编写 JNI 或其他 FFI 代码。
该工具可能对希望将 Rust 用作跨平台语言并为每个平台导出多个工件的人很有用。
快速入门
假设您正在用 Rust 编写两个服务,并在 iOS 和 Android 中调用它们。
首先,您需要编写几个代表您服务的 Rust 特性。
pub trait Services: Send + Sync {
fn get_login_service() -> Box<dyn LoginService>;
fn get_upload_service() -> Box<dyn UploadService>;
}
pub trait LoginService: Send + Sync {
fn login(&self, user_name: String, pwd: String) -> Box<dyn Future>;
}
pub trait Future: Send + Sync {
fn get(&self) -> bool;
}
pub trait UploadService: Send + Sync {
fn upload(&self, path: String, listener: Box<dyn UploadProgress>) -> i64;
}
pub trait UploadProgress : Send + Sync {
fn on_progress(&self, id: i64, process: i64, total: i64);
}
然后,您可以实现您的特性以实现服务。
pub struct ServiceHolder {}
impl Services for ServiceHolder {
fn get_login_service() -> Box<dyn LoginService> {
Box::new(LoginServiceImp {})
}
fn get_upload_service() -> Box<dyn UploadService> {
Box::new(UploadServiceImp {})
}
}
pub struct LoginServiceImp {}
impl LoginService for LoginServiceImp {
fn login(&self, user_name: String, pwd: String) -> Box<dyn Future> {
struct FutureImp {
pub user_name: String,
pub pwd: String,
}
impl Future for FutureImp {
fn get(&self) -> bool {
let handle = thread::spawn(move || {
// do your login
true
});
handle.join().unwrap()
}
}
Box::new(FutureImp { user_name, pwd })
}
}
pub struct UploadServiceImp {}
impl UploadService for UploadServiceImp {
fn upload(&self, path: String, listener: Box<dyn UploadProgress>) -> i64 {
thread::spawn(move || {
// doing uploading
listener.on_progress(99999, 10, 12345);
});
99999
}
}
之后,运行 rsbind 命令生成 iOS 和 Android 库工件。
rsbind . android all
rsbind . ios all
然后,使用 iOS 库,您可以直接从 Swift 调用服务。
let loginService = RustLib.newServices().getLoginService();
let future = loginService.login(user_name: "sidney.wang", pwd: "88888888")
let result = future.get();
print("login result = \(result)")
class Listener : UploadProgress {
func onProgress(id: Int64, process: Int64, total: Int64) {
print("Progress is \(process)/\(total)")
}
}
let uploadService = RustLib.newServices().getUploadService();
uploadService.upload(path: "to/your/path", listener: Listener())
在 Android 中,操作非常类似,只需运行 Java 代码。
LoginService loginService = RustLib.newServices().getLoginService();
Future future = loginService.login("sidney.wang", "88888888");
boolean result = future.get();
Log.i(TAG, "login result is " + result);
UploadService uploadService = RustLib.newServices().getUploadService();
uploadService.upload("to/your/path", new UploadProgress() {
@Override
public void onProgress(long id, long process, long total) {
Log.i(TAG, "upload process is " + process);
}
});
逐步进行。
- 设置 Rust 环境.
- 安装 'rsbind'。
cargo install --git https://github.com/rs-bind/rsbind.git --force -- rsbind
- 创建一个 Rust 库,其中包含用于公开您的服务的模块。
- 结构: rsbind 模块是您公开服务的地方。在这个结构中,API 和实现都在一个模块中,rsbind 将解析所有特性和 impl,并生成绑定代码。您需要将不必要的代码移到其他文件中。
也许您想将 API 和实现分开,那么您可以使用这些结构。
- 第一个结构
- 第二结构
您可以将接口放入合同模块,实现放入imp模块。在lib.rs中公开这两个模块。
// such as your code in contract dir as below:
pub trait YourContract : Send + Sync {
fn test_simple(arg1: i32, arg2: String) -> String;
fn test_callback(arg: Box<dyn Callback>);
fn test_struct() -> StructSimple;
fn test_return_callback() -> Box<dyn Callback>;
}
pub trait Callback : Send + Sync {
fn on_callback(&self, arg1: i64, arg2: String);
}
pub struct StructSimple {
pub arg3: String,
pub arg4: bool,
}
// Your implementation is as below
pub struct YourImplemetation {}
impl YourContract for YourImplemetation {
fn test_simple(arg1: i32, arg2: String) -> String {
format!("Your test_simple result is {}_{}", arg1, arg2)
}
fn test_callback(arg: Box<dyn Callback>) {
arg.on_callback(123i64, "hello callback".to_owned());
}
fn test_struct() -> StructSimple {
StructSimple {
arg1: "struct".to_owned(),
arg2: true
}
}
fn test_return_callback() -> Box<dyn Callback> {
struct Instance{}
impl Callback for Instance {
fn on_callback(&self, arg1: i64, arg2: String) {
}
}
Box::new(Instance{})
}
}
- 按照以下方式运行rsbind命令。然后生成的代码将在_gen目录下,aar/framework将在target目录下。
Rsbind用法
rsbind path-of-project android/ios/mac/jar/all ast/bridge/artifact/header/build/all
- ast: 使用json格式生成简化的ast文件到_gen/ast。
- bridge: 生成C方法,以将我们的接口暴露到_gen/[ios/android/mac/jar]_bridge。
- artifact: 生成java/swift包装器和C头文件,然后将它们放入项目(_gen/[ios/android/mac/jar]_artifact)中。
- build: 构建桥接模块,将输出复制到artifact项目,然后构建artifact项目。
- all: 运行绑定步骤的所有步骤。
- 它将生成包含在aar或cocoapods库中的java文件,然后您可以将它们集成到您的android/iOS项目中并调用函数。对于android,您可以像下面这样调用
YourContract instance = RustLib.newYourContract();
instance.testCallback(new Callback(){
void onCallback(long arg1, String arg2) {
// do your things.
}
})
Swift非常相似。
配置
您可以为添加一些配置创建一个名为Rsbind.toml的文件。
[android]
rustc_param = ""
arch = ["armv7-linux-androideabi", "aarch64-linux-android", "i686-linux-android"]
release = true
namespace = "com.afoxer.xxx.ffi"
so_name = "demo"
ext_lib = []
features_def = ["xxxx=[]"]
[ios]
rustc_param = ""
arch = ["armv7-apple-ios", "i386-apple-ios", "x86_64-apple-ios"]
release = true
features_def = []
[mac]
rustc_param = ""
release = true
features_def = []
[jar]
rustc_param = ""
release = true
namespace = "com.afoxer.xxx.ffi"
so_name = "demo"
#ext_lib = []
#features_def = ["xxxx=[]"]
支持的类型
- 参数:基本类型,回调,Vec
- 返回:基本类型,结构体,回调,Vec
回调中支持的类型
- 参数:基本类型,Vec,结构体
- 返回:基本类型,Vec。
将在近期支持它。
定义回调和普通特质不同。它应该在每个回调中包含&self,但不包含在普通特质中。
回调
pub trait Callback : Send + Sync {
fn on_callback(&self, arg1: i32, arg2: String, arg3: bool, arg4: f32, arg5: f64) -> i32;
fn on_callback2(&self, arg1: bool) -> bool;
fn on_callback_complex(&self, arg1: StructSimple) -> bool;
fn on_callback_arg_vec(&self, arg1: Vec<StructSimple>) -> bool;
fn on_callback_arg_vec_simple(&self, arg1: Vec<String>) -> bool;
}
普通特质
pub trait TestContract1 : Send + Sync {
fn test_arg_vec(arg: Vec<String>) -> i32;
fn test_return_vec(arg: u8) -> Vec<i32>;
fn test_arg_callback(arg: Box<dyn Callback>) -> u8;
fn test_bool(arg1: bool) -> bool;
fn test_struct() -> StructSimple;
fn test_struct_vec() -> Vec<StructSimple>;
}
依赖项
~13–24MB
~354K SLoC