JNI基础(3): Java中调用JNI方法

2022-01-10  本文已影响0人  MemetGhini

3.1 Java中加载动态库

java中通过系统提供的如下方法来加载动态库。

static {
    System.loadLibrary("cmakecompile");
}

ps: java中的static代码块随着类的加载而执行,并且只会执行一次,并且还优先于main函数。
其实只需要在调用JNI方法之前load就行,可以捕获一下异常来检查是否加载成功,加载不成功就不能调用native方法 否则肯定会出现找不到方法而闪退。

try {
    System.loadLibrary("cmakecompile");
} catch (Throwable e) {
    e.printStackTrace();
}

3.2 JNI中的JNIEnv和JavaVM的关系

两者中JavaVM是在各个线程中共享。android只允许有一个JavaVM。在不同线程中需要JNIEnv*时可以通过JavaVM获取。所以可以考虑全局存储JavaVM。 Java 的dex字节码和c/c++的.so同时运行Dalvik虚拟机之内,共同使用一个进程空间。之所以可以相互调用,也是因为有Dalvik虚拟机。

3.3 Java中调用Native方法方式,命名规则和JNI头文件创建方式

java调用native方法 首先需要用native关键字声明java方法。例如:

public native String intFromJNI();

此时Android Studio会提示找不到这个方法的JNI实现,我们点击提示的创建该方法的JNI实现选择实现文件就会自动添加进去,然后我们去实现相关的C++实现即可。先看看自动添加的代码:

extern "C" JNIEXPORT jstring JNICALL Java_com_memetghini_cmakecompile_MainActivity_intFromJNI(JNIEnv *env, jobject this) {

}

首先extern "C"是因为c/c++编绎器对函数名译码的方式不同所引起的。c语言的函数是通过函数名来识别的,但c++有方法重载等问题所以函数编译后生成的符号里面体现了函数的名字,参数类型,返回值类型等信息,产物就不一样。所以就算是一样的代码通过c和c++编译器编译之后连接方式就不太一样。所以这里extern "C"是为了避免c++编译按照c++的方式编译c函数,这里jni函数就是一个c函数。可以通过用__cplusplus宏来包围的形式同时支持c编译来编译是忽略这个extern c,因为c编译压根就不认识这个。

#ifdef __cplusplus
extern "C" {
#endif

jni methods

#ifdef __cplusplus
}
#endif

JNIEXPORT 显然就是要导出此方法。Windows平台中需要导出动态库的方法需要#define JNIEXPORT __declspec(dllexport)。如果此方法不在导出的符号表中JNI也无法调用它。例如在mac平台下可以不加,默认会导出。如果替换为__attribute__ ((visibility ("hidden"))) jint JNICALL就找不到了。

JNICALL在windows中的值为__stdcall,用于约束函数入栈顺序和堆栈清理的规则。在linux,mac平台下是一个空声明。所以简单来说在Linux平台上这两个宏不加也没有影响。

方法命名规则是:Java开头然后拼接包名再拼接类名最后是方法名,每一个都用下划线_连接即可。默认带两个参数。

上一篇下一篇

猜你喜欢

热点阅读