Android Rust JNI系列教程(三) Rust与Android互相调用

news2024/11/23 12:38:03

前言

RustJNI流程以及方法实际上和我们常见的C++ JNI是十分相似的.我们本章将使用Rust实现常见的JNI调用功能.关于更多的用法,可参考官方示例,github地址为https://github.com/jni-rs/jni-rs/blob/master/example/mylib/src/lib.rs.

基本交互功能实现

1. Java传String,返回byte数组

  1. 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很好的提供了JStringrust String方法以及&[u8]jbyteArray方法.
我们在Android里面测试一下:

  1. Android层声明一个native方法
public static native byte[] getByteFromString(String java_str);
  1. 调用
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. 回调(同步)

  1. 首先在Android层新建一个interface,本文为RustListener
public interface RustListener {
    void onStringCallback(String msg);

    void onVoidCallback();
}

interface定义了两个回调函数onStringCallbackonVoidCallback,其中onStringCallback传回一个String,onVoidCallback则不回传任何参数.

  1. Android层声明的native方法
public static native void  syncCallback(RustListener listener);
  1. 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");
           }
 }); 
  1. 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对象.
  • 在这段,我们又了解到了rust StringJString的方法let j_string_hello = env.new_string(jni_string_hello).unwrap().结合上一小节的JStringrust String.我们已经完整学习了rustAndroid直接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. 回调(异步)

  1. 依然使用上一节的RustListener,声明native方法
public static native void  asyncCallback(RustListener listener);
  1. 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");
         }
});
  1. 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单例方法

  1. 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();
    }
}
  1. 添加一个方法,供rust调用
 public void logIdentityHashCode(){
     Log.d("NativeSingleton",System.identityHashCode(this)+" ");
 }
  1. 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());这部分代码是为了实现JValueJObject的转化,此方法是我在jni源码里找的,不知道会不会有更清晰的转化方法.
  • 本段也演示了call_static_method_uncheckedcall_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,结果会在当前目录下生成一个依赖图
dep
cargo-deps可以配合cargo tree使用,帮助我们更好的分析依赖情况.

如果遇到没有cargo deps命令的情况,安装即可.安装命令如下:

cargo install cargo-deps

总结

本章主要介绍了androidrust进行互相调用的基础知识,包括常见的数据类型,回调,单例等.更多玩法请参考官方示例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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/91575.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java集合复习

文章目录集合概述、collection集合体系特点Collection集合的遍历增强for循环集合概述、collection集合体系特点 集合都是支持泛型的&#xff0c;但是集合只能存储对象&#xff0c;因此集合也叫做对象集合。 public static void main(String[] args) {Collection<String>l…

分布式 | 令人头疼的堆外内存泄露怎么排查?

作者&#xff1a;鲍凤其 爱可生 dble 团队开发成员&#xff0c;主要负责 dble 需求开发&#xff0c;故障排查和社区问题解答。少说废话&#xff0c;放码过来。 本文来源&#xff1a;原创投稿 *爱可生开源社区出品&#xff0c;原创内容未经授权不得随意使用&#xff0c;转载请联…

OpenFace Win10 运行和抽离部分代码

需求&#xff1a;提取出OpenFace中的GazeAnaLyser 中的部分代码往一个写好的接口里面放&#xff0c;主要实现提取面部的所有关键点&#xff0c;估计出视线的功能&#xff1b; 一&#xff0c;openface的安装与使用 在win10上把openface跑起来这个链接够用了&#xff0c;这里主要…

非零基础自学Golang 第5章 流程控制 5.2 循环控制

非零基础自学Golang 文章目录非零基础自学Golang第5章 流程控制5.2 循环控制5.2.1 for循环5.2.2 break 跳出循环5.2.3 continue 继续循环第5章 流程控制 5.2 循环控制 5.2.1 for循环 Go语言中的循环逻辑通过for关键字实现。不同于其他编程语言&#xff0c;Go语言没有while关…

NeurIPS2022 | OmniVL: 用于Image-Language和Video-Language任务的通用模型

原文标题&#xff1a;OmniVL: One Foundation Model for Image-Language and Video-Language Tasks 论文链接&#xff1a;OmniVL: One Foundation Model for Image-Language and Video-Language Tasks | OpenReview 三模态统一的工作。 一、问题提出 旨在设计一个全视觉语言…

正点原子基于库和寄存器建立keil的工程文件简单理解(不是具体步骤)

下载mdk 个人上传免费的pdf:https://download.csdn.net/download/weixin_43794311/87232741&#xff0c;或直接到正点原子官网下载 https://www.keil.com/download/ 两种建立方式的本质理解 库函数是有人基于寄存器已经完成对寄存器的设置&#xff0c;只要根据函数参数意义&…

【Uni-App】vscode 开发uni-app 配置eslint、prettier 实现代码检查和代码自动格式化

目录一&#xff1a;前言二、利用HBuilderX创建uni-app项目三、配置代码检查和代码自动格式化1. 在vscode中打开项目2. 创建package.json3. 添加eslint、prettier相关依赖4. 配置.eslintrc.js5. 配置.prettierrc.json6. 配置.editorconfig7. 配置.eslintignore如果以上还不生效&…

USB——域,包,事务及传输

USB 域&#xff0c;包&#xff0c;事务及传输 域组成包包组成事务事务组成传输 字节序 LSB 概览 域通常来说有八个 SYNCPIDADDRENDP&#xff1a;FRAMEDATACRCEOP 多个域组成包&#xff0c;USB 的包分为四大类 token&#xff1a;令牌包data&#xff1a;数据包handshake&…

centos7安装samba

关闭防火墙 [rootlocalhost ~]# systemctl stop firewalld.service [rootlocalhost ~]# systemctl disable firewalld.service Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service. Removed symlink /etc/systemd/system/basic.target.wants/f…

单目测距(yolo目标检测+标定+测距代码)

单目测距&#xff08;目标检测标定测距&#xff09;** 实时感知本车周围物体的距离对高级驾驶辅助系统具有重要意义&#xff0c;当判定物体与本车距离小于安全距离时便采取主动刹车等安全辅助功能&#xff0c;这将进一步提升汽车的安全性能并减少碰撞的发生。上一章本文完成了目…

Shell 标准输入和输出

无论是要交给程序处理的数据&#xff0c;还是控制脚本的简单命令&#xff0c;都少不了输入和输出。程序要做的第一件事就是处理如同一阴一阳的“输入与输出”。 1 、从文件获取输入 当我们希望向文件输出内容时&#xff0c;我们可以通过符号 > 或 >> 实现。而用代表…

[ Linux ] 线程独立栈,线程分离,Linux线程互斥

目录 1.线程栈 1.1pthread_t 1.2用户级的线程id与内核LWP的对应关系 2.分离线程 2.1 pthread_detch 3.线程互斥 3.1互斥相关概念 3.2 互斥量mutex 3.3 售票系统案例验证共享变量会有问题 3.4 解决抢票问题 3.5互斥量的接口 3.5.1初始化互斥量 3.5.2 销毁互斥量 3…

MSF之ssh_login漏洞

ssh_login准备实操准备 目标机&#xff1a;windows xp 攻击机&#xff1a;kali 工具&#xff1a;metasploit framework 实操 先查看两机器的ip kali的ip为172.17.0.1 xp的ip为192.168.17.130 互相ping一下 没问题。 打开msf search ssh_login 爆出模块 use 0 show o…

vue项目打包流程与反向代理Nginx的使用

目录 前言 参考文章 正文 1.打包前的配置工作 做反向代理的原因&#xff08;Vue项目打包后Proxy失效的问题&#xff09;&#xff1a; 2.Nginx使用 前言 突发灵感想学习下打包&#xff0c;第一反应是学习webpack&#xff0c;翻找一通后发现用不着webpack&#xff0c;因为…

ORB-SLAM2 --- Tracking::UpdateLocalPoints函数

目录 1.函数作用 2.函数流程 3.code 4.函数解析 1.函数作用 更新局部关键点。先把局部地图清空&#xff0c;然后将局部关键帧的有效地图点添加到局部地图中。 2.函数流程 这是更新局部地图中的一个小函数&#xff0c;我们在Tracking::UpdateLocalKeyFrames更新了局部关键…

C++ Reference: Standard C++ Library reference: Containers: map: map: value_comp

C官网参考链接&#xff1a;https://cplusplus.com/reference/map/map/value_comp/ 公有成员函数 <map> std::map::value_comp value_compare value_comp() const;返回值比较对象 返回一个比较对象&#xff0c;该对象可用于比较两个元素&#xff0c;以获得第一个元素的键…

Python 基础| Python 直接赋值、深拷贝和浅拷贝

先看这三个词的意思我觉得菜鸟的总结就很好 Python 直接赋值、浅拷贝和深度拷贝 | 菜鸟教程 直接赋值&#xff1a;其实就是对象的引用&#xff08;别名&#xff09;。 浅拷贝(copy)&#xff1a;拷贝父对象&#xff0c;不会拷贝对象的内部的子对象。 深拷贝(deepcopy)&#xf…

采购过程中会遇到的四种风险!如何管理和控制?

采购风险通常是指采购过程可能出现的一些意外情况&#xff0c;这些情况都会影响采购预期目标的实现。采购风险通常是由管理不善引起的&#xff0c;本文解释了采购过程中会遇到的四种风险&#xff0c;并介绍通过正确实施8Manage SRM采购管理系统&#xff0c;可以有效管理和控制它…

说说Python程序的执行过程

1. Python是一门解释型语言&#xff1f; 我初学Python时&#xff0c;听到的关于Python的第一句话就是&#xff0c;Python是一门解释性语言&#xff0c;我就这样一直相信下去&#xff0c;直到发现了*.pyc文件的存在。如果是解释型语言&#xff0c;那么生成的*.pyc文件是什么呢&…

工程项目管理的特点

工程项目管理是一种只关注工程项目的项目管理。它使用与任何其他类型的项目管理相同的标准方法和流程。这种专业化可能会吸引任何想要进入项目管理领域的具有工程背景的人。 工程项目管理与工程管理 工程管理侧重于对具有以下特点的工程师和工程任务的管理&#xff1a; 1、…