前言
Rust的JNI流程以及方法实际上和我们常见的C++ JNI是十分相似的.我们本章将使用Rust实现常见的JNI调用功能.关于更多的用法,可参考官方示例,github地址为https://github.com/jni-rs/jni-rs/blob/master/example/mylib/src/lib.rs.
基本交互功能实现
1. Java传String,返回byte数组
rust代码如下:
#[no_mangle]
pub extern "system" fn Java_com_jni_rust_RustNative_getByteFromString(env: JNIEnv, _: JClass, java_str: JString) -> jbyteArray {
let input: String = env.get_string(java_str).unwrap().into();
let input_array = input.as_bytes();
let output = env.byte_array_from_slice(input_array).unwrap();
output
}
jni很好的提供了JString转rust String方法以及&[u8]转jbyteArray方法.
我们在Android里面测试一下:
Android层声明一个native方法
public static native byte[] getByteFromString(String java_str);
- 调用
byte[] resultArray = RustNative.getByteFromString("this is a java str");
Log.d(TAG, Arrays.toString(resultArray));
Log.d(TAG, new String(resultArray));
将rust生成的so文件放到Android工程的libs/arm64-v8a下,测试一下结果.
完整代码将放在文末.
2. 回调(同步)
- 首先在
Android层新建一个interface,本文为RustListener
public interface RustListener {
void onStringCallback(String msg);
void onVoidCallback();
}
interface定义了两个回调函数onStringCallback和onVoidCallback,其中onStringCallback传回一个String,onVoidCallback则不回传任何参数.
Android层声明的native方法
public static native void syncCallback(RustListener listener);
Android层调用
RustNative.syncCallback(new RustListener() {
@Override
public void onStringCallback(String msg) {
Log.d(TAG, "sync callback: " + msg);
}
@Override
public void onVoidCallback() {
Log.d(TAG, "sync callback");
}
});
rust代码
#[no_mangle]
pub extern "system" fn Java_com_jni_rust_RustNative_syncCallback(env: JNIEnv, _: JClass, callback: JObject) {
let hello = "hello syncCallback";
let jni_string_hello = JNIString::from(hello);
let j_string_hello = env.new_string(jni_string_hello).unwrap();
let j_value_hello = JValue::from(j_string_hello);
env.call_method(callback, "onStringCallback", "(Ljava/lang/String;)V", &[j_value_hello]).unwrap();
env.call_method(callback, "onVoidCallback", "()V", &[]).unwrap();
}
- 其中,
callback: JObject即是我们从Android层传过来的RustListener对象. - 在这段,我们又了解到了
rustString转JString的方法let j_string_hello = env.new_string(jni_string_hello).unwrap().结合上一小节的JString转rustString.我们已经完整学习了rust与Android直接String类型互相转化的方式. - 再看看
rust调用Android方法的函数env.call_method(obj,name,sig,args).
obj:java对象.
name:Android方法名.
sig: 方法签名.
args: 是一个&[JValue],它就是我们的参数列表了.如果不含参数,则放置一个空的数组即可&[]. - 还有一个
rust调用Android方法的函数call_method_unchecked,整体流程和我们C++方式调用Android方法的流程非常相似,大家有兴趣可以自行进一步了解,我们也将在后面第4小节的例子中简单使用一下.
3. 回调(异步)
- 依然使用上一节的
RustListener,声明native方法
public static native void asyncCallback(RustListener listener);
Android端调用
RustNative.asyncCallback(new RustListener() {
@Override
public void onStringCallback(String msg) {
Log.d(TAG, "async callback: " + msg);
}
@Override
public void onVoidCallback() {
Log.d(TAG, "async callback");
}
});
rust代码
#[no_mangle]
pub extern "system" fn Java_com_jni_rust_RustNative_asyncCallback(env: JNIEnv, _: JClass, callback: JObject) {
let jvm = env.get_java_vm().unwrap();
let callback = env.new_global_ref(callback).unwrap();
let (tx, rx) = mpsc::channel();
let _ = thread::spawn(move || {
tx.send(()).unwrap();
let env = jvm.attach_current_thread().unwrap();
let hello = "hello syncCallback";
let jni_string_hello = JNIString::from(hello);
let j_string_hello = env.new_string(jni_string_hello).unwrap();
let j_value_hello = JValue::from(j_string_hello);
for _i in 0..6 {
env.call_method(&callback, "onStringCallback", "(Ljava/lang/String;)V", &[j_value_hello]).unwrap();
thread::sleep(Duration::from_millis(2000));
}
});
rx.recv().unwrap();
}
这段代码的功能是每2s回调一次onStringCallback.下面我们简要的分析一下代码.
-
let jvm = env.get_java_vm().unwrap();
由于JNIEnv不能跨线程使用, 所以在这里先获取JavaVM,在线程内再由JavaVM来获取JNIEnv. -
let callback = env.new_global_ref(callback).unwrap();
获取一个callback的全局引用,防止callback被回收. -
let env = jvm.attach_current_thread().unwrap();
使用JavaVM接口将JNIEnv附加到当前线程.
后续就可以正常使用JNIEnv调用Android方法了.
4. rust调用Android单例方法
- 在
Android层新建一个单例类
public class NativeSingleton {
private NativeSingleton() {
}
public static NativeSingleton getInstance() {
return NativeSingleton.SingletonHolder.sInstance;
}
private static class SingletonHolder {
private static final NativeSingleton sInstance = new NativeSingleton();
}
}
- 添加一个方法,供
rust调用
public void logIdentityHashCode(){
Log.d("NativeSingleton",System.identityHashCode(this)+" ");
}
rust调用单例方法
#[no_mangle]
pub unsafe extern fn Java_com_jni_rust_RustNative_singleton(env: JNIEnv, _: JClass) {
let clz = match env.find_class("com/jni/rust/NativeSingleton") {
Ok(class) => { class }
Err(_) => {
panic!("can't find class NativeSingleton");
}
};
let instance_method_id = match env.get_static_method_id(clz, "getInstance", "()Lcom/jni/rust/NativeSingleton;") {
Ok(ins) => {ins}
Err(_) => {
panic!("can't find method NativeSingleton.getInstance");
}
};
let instance = match env.call_static_method_unchecked(clz, instance_method_id, ReturnType::Object, &[]) {
Ok(obj) => {obj}
Err(_) => {
panic!("can't call method getInstance");
}
};
let instance_obj = JObject::from(instance.l().unwrap());
let log_identity_hashcode = match env.get_method_id(clz, "logIdentityHashCode", "()V") {
Ok(get) => {get}
Err(_) => {
panic!("can't call method logIdentityHashCode");
}
};
env.call_method_unchecked(instance_obj, log_identity_hashcode, ReturnType::Primitive(Void), &[]).unwrap();
}
- 为了演示,本段代码使用了
match表达式,在遇到Err时直接做了panic.在实际应用中最好能做一些处理,使整个项目更健壮些. let instance_obj = JObject::from(instance.l().unwrap());这部分代码是为了实现JValue到JObject的转化,此方法是我在jni源码里找的,不知道会不会有更清晰的转化方法.- 本段也演示了
call_static_method_unchecked和call_method_unchecked方法的使用,如遇相同场景大家可以拿来参考.
扩展知识
1. 显示依赖关系树
cargo tree
显示结果
$ cargo tree
rust_jni_demo v0.1.0 (/home/txs/xxx/xxx/rust_jni_demo)
├── android_logger_lite v0.1.0
└── jni v0.20.0
├── cesu8 v1.1.0
├── combine v4.6.6
│ ├── bytes v1.3.0
│ └── memchr v2.5.0
├── jni-sys v0.3.0
├── log v0.4.17
│ └── cfg-if v1.0.0
└── thiserror v1.0.37
└── thiserror-impl v1.0.37 (proc-macro)
├── proc-macro2 v1.0.47
│ └── unicode-ident v1.0.5
├── quote v1.0.21
│ └── proc-macro2 v1.0.47 (*)
└── syn v1.0.105
├── proc-macro2 v1.0.47 (*)
├── quote v1.0.21 (*)
└── unicode-ident v1.0.5
[build-dependencies]
└── walkdir v2.3.2
└── same-file v1.0.6
依赖关系一目了然.
cargo-deps
具体命令cargo deps | dot -Tpng > dep.png,结果会在当前目录下生成一个依赖图

cargo-deps可以配合cargo tree使用,帮助我们更好的分析依赖情况.
如果遇到没有cargo deps命令的情况,安装即可.安装命令如下:
cargo install cargo-deps
总结
本章主要介绍了android与rust进行互相调用的基础知识,包括常见的数据类型,回调,单例等.更多玩法请参考官方示例https://github.com/jni-rs/jni-rs/blob/master/example/mylib/src/lib.rs.
Android项目地址:https://github.com/tangxuesong6/Android_Rust_JNI_Demo
rust项目地址:https://github.com/tangxuesong6/Rust_JNI_Demo











![[ Linux ] 线程独立栈,线程分离,Linux线程互斥](https://img-blog.csdnimg.cn/img_convert/57345c24fe88920e4284bbdbe2a681bd.png)






