0%

【知识总结】 jni用法详解

学习背景

最近由于公司项目的需要,要求把一个第三方c语言的so库封装成java接口,并打包成jar,最终运行在linux平台。

整个学习和封装实现大约花了半个月,中间遇到了各种问题,特此总结。

出于公司项目安全的要求,后续总结不给出实际的文件命名,而重新用一些简单的替换命名来表述。

实现流程

  • 已有一个第三方so库(a.so)、对应的头文件(记为a.h)
  • 根据头文件自行定义对应的java接口,a_interface.java,编写文档
  • 实现对应的a_imp.java源文件
    • 类初始化时需要加载jni格式c文件编译生成的so库(jni.so)
      • 如果有依赖库要先加载对应依赖库
    • public 前缀修饰符对应 a_interface.java接口定义的实现
    • private native 前缀修饰符对应本地已有函数的声明,该函数的实现在jni.so中
      • 和public一一对应
  • 命令行执行javah -jni -encoding UTF-8 [包名.子包名.a_interface.java]
    • 将生成包名_子包名_a_interface.h jni格式的头文件
  • 创建并实现包名_子包名_a_interface.c jni格式的源文件
    • 需要包含a.h
    • 需要加载a.so
      • 编译配置推荐使用Makefile或CMakeLists实现
    • 在jni格式源文件中调用a.so中的接口,并根据a_imp.java的private native函数的形式来返回变量
  • 编译生成jni.so
  • 在a_imp.java对应的java项目中配置打包,导出a.jar包
  • 编写java测试demo,导入jar包、相关so库,调用a_interface的接口

调用流程

这里考虑String作为入参和返回的情况

  • 外部使用者调用a_imp.java的接口,传入String对象
  • public 把String对象传入对应的private native 函数中
  • native函数调用jni.so对应函数,此时java中的String对象将自动转换为c语言的jString结构体变量
  • 调用jni相关转换函数,把jString转换为char数组
  • 把char数组作为入参传入到a.so对应的函数中,得到返回的char数组
  • 利用jni相关转换函数,把char数组转化为jString变量并返回
  • native函数也就返回了java的String对象
  • public接口返回String对象

此外如果入参是java自定义类,则对应jni的jObject结构体;

如果入参是int,则对应jni的jint类型;

jni提供了完善的类型转换接口,可自行搜索资料