JNI中"No implementation found for native"错误的原因总结

说明

最近由于工作需要,我需要将C++库封装打包成AAR给Android项目使用,因此接触了JNI技术。由于完全没有经验,我在使用过程中遇到了不少坑,耗费了不少时间。
在我遇到的JNI错误中,其中最常遇到的一个报错便是在运行时提示”No implementation found for native”错误,错误信息类似于

No implementation found for void com.xxx.xxx.xxx.xxx(tried Java_com_xxx_xxx_xxx_xxx and Java_com_xxx_xxx_xxx_xxx_xxx)

导致这个错误的原因有很多,我也踩了其中不少的坑。为了避免以后再犯同样的错误,通过这篇文章记录总结一下我遇到的部分原因。

注:为了简洁,本文使用了部分可能不太合适的术语,具体如下

术语 说明
native方法 Java文件中声明的native方法
native库 包含native方法实现的C/C++库
JNI声明 native方法对应的C方法的声明
JNI实现 native方法对应的C方法的实现
JNI头文件 包含native方法对应的C方法声明的C头文件
JNI源文件 包含native方法对应的C方法实现的C源文件

原因

未实现native方法

第一种原因也是最容易想到的,就是提示出错的方法确实没有在C/C++代码中声明或实现,只需要native方法和JNI声明以及实现一一比对,很容易找出问题。

为了有效避免这个原因,我的建议有:

  • 应该尽可能使用javah来生成JNI头文件,可以通过配置Android Studio中的External Tools来方便使用
  • 确保每一个JNI方法均得到了实现
  • 在修改了native方法后,第一时间修改对应的JNI声明和JNI实现

JNI声明不对应

由于JNI声明极其复杂,若是手动添加或修改JNI声明,稍有不慎便可能导致native方法和JNI声明不匹配。由于出错后会在括号中提示tried Java_com_xxx,可以对照进行比对,同时还要重点检查返回值和参数是否对应。在Android Studio中,可以通过Ctrl + 鼠标左键点击native方法跳转的方式检查是否对应。若存在对应的JNI声明和JNI实现,应该会有两处可跳转位置

JNI声明未使用extern "C"包含

由于C++的函数存在name mangling,而JNI在加载方法时需要通过方法名来加载对应的方法,因此每一个JNI声明均需要包含在extern "C"的作用范围内,否则JNI无法查找到正确的方法地址

JNI源文件中未包含JNI头文件

这是我遇到的第一个导致此问题的原因。如果在JNI源文件中未include对应的JNI头文件,则JNI源文件中的JNI实现会被当做普通的C++方法,而JNI头文件中的声明则被认为是未实现的方法

Java代码中未加载native库

由于Android使用动态加载库的方式在运行期打开、读取native库,而不是像C/C++在编译期直接链接native库。因此如果在使用native方法时尚未对native库进行加载,则同样会导致方法未实现的报错。未加载native库的原因可能有

  • 未调用system.LoadLibrary("xxx")或相关的加载动态库的方法
  • 动态库的名称有误,特别注意的是如果使用的是system.LoadLibrary方法,编译出来的native库文件名称需要是lib + 库名 + 后缀,其中方法的参数只需要传入库名即可。
  • 在调用native方法是,尚未执行加载动态库的方法。如项目中有A.javaB.java两个Java文件,其中均含有native方法,但仅在A.java中对native库进行了加载,那么在尚未使用A.java时,native库是不会加载的,此时如果先使用了B.java中的native方法,同样会发生方法未实现的错误。为了避免这种情况,由于JNI在加载native库的时候会检查要加载的native库是否已经加载,因此推荐在所有可能被用户直接使用的包含native方法的Java类中,均添加如下代码,这样可以保证在使用该类之前native库已被加载
1
2
3
static {
System.loadLibrary("native-lib");
}

总结

其实仔细想想,造成这个错误的原因大多是由于一些细节问题导致的,但由于原因比较多,而且JNI代码本身显得有些杂乱,因而特别容易出错,而且往往需要花一定时间去找到问题。本文仅仅列举了我遇到的几种可能造成该问题的原因,无法概括全部的原因。但最诡异的问题往往是由最简单的错误造成的。因此保持细心,理清思路,便能尽可能避免这些错误的发生。

参考

文章目录
  1. 1. 说明
  2. 2. 原因
    1. 2.1. 未实现native方法
    2. 2.2. JNI声明不对应
    3. 2.3. JNI声明未使用extern "C"包含
    4. 2.4. JNI源文件中未包含JNI头文件
    5. 2.5. Java代码中未加载native库
  3. 3. 总结
  4. 4. 参考
|