文章目录
JNI中的JNIEXPORT、JNIIMPORT和JNICALL
两个关键字的定义都可以在jni_md.h下找到
均用于定义与平台相关的宏
用于标识函数用途
- JNIEXPORT:(实则为C++规则)放置在函数、变量或对象的声明前面,指示编译器将其导出为动态链接库的一部分。这使得其他程序可以通过在运行时加载动态链接库并使用导出的函数、变量或对象
- JNIIMPORT:(实则为C++规则)放置在函数、变量或对象的声明前面,指示编译器将其标记为从动态链接库中导入的符号
- JNICALL:(实则为C++规则)一种标准的函数调用约定,也被称为 “标准调用”;具有以下性质:
函数的参数按照从右到左的顺序依次入栈。这意味着最右边的参数首先被压入栈中。
调用方清理栈上的参数。这意味着在函数调用结束后,由调用方负责从栈上移除函数的参数。
函数的返回值通常存储在 EAX 寄存器中
//Windows下的定义
#define JNIEXPORT __declspec(dllexport)
#define JNIIMPORT __declspec(dllimport)
#define JNICALL __stacall
//Linux下的定义(实际是空定义)
#define JNIEXPORT
#define JNIIMPORT
#define JNICALL
JVM如何查找native方法
①按照JNI规范的命名规则
即根据JNI所约定的命名规则来指定函数的命名,具体规则如下:Java_类全路径_方法名
JNIEXPORT jstring JNICALL Java_com_kqli_jni_JniTest_getStringFromC(JNIEnv *env, jclass jclass);
JNIEXPORT jstring JNICALL Jave_com_test_jni_HelloWord_func(JNIEnv* env, jclass class, jstring str);
- 第一个jstring为返回值(string类型)
- 函数名中com_kqli_jni_JniTest_getStringFromC,代表Java类com_kqli_jni_JniTest,getStringFromC代表具体的函数名称
JNIEnv,指向JVM函数表的指针 - jclass,调用Java中native方法的实例对象
②调用JNI提供的RegsterNatives函数,将本地函数注册到JVM中
//函数原型
jint RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods);
- clazz:声明native方法的类
- methods:JNINativeMethod结构的数组
typedef struct {
char *name; //java方法名称
char *signature;//java方法签名
void *fnPtr;//c/c++的函数指针
} JNINativeMethod;
- nMethods:指定methods数组中的本地方法数,通常写法为
nMethods = sizeof(methods) / sizeof(JNINativeMethod);
示例代码
java
package com.test.jni;
public class A{
static{
System.loadLibrary("A");
}
public static native int a(String str);
public static native boolean b();
public static native int c(Object obj);
public static void main(String[] args){
......
}
}
native
jint a(JNIEnv *env ,jclass class, jstring str){
....
}
jboolean b(JNIEnv *env ,jclass class){
....
}
jint c(JNIEnv *env ,jclass class, jobject obj){
....
}
static JNINativeMethod method_table[] =
{
{"a", "(Ljava/lang/String;)I", (void *)a},
{"b", "()Z", (void *)b},
{"c", "(Ljava/lang/Object;)I", (void *)c},
};
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
......
jclass clz = env ->FindClass(JNIREG_CLASS);
env ->RegisterNatives(clz, method_table, sizeof(method_table) / sizeof(JNINativeMethod));
......
}
JNI数据类型
类: 例如String的签名为Ljava/lang/String; 注意: 包名和类名用/隔开, 结尾有一个;
数组:用[表示数组签名, 例如int[]的签名为[I
JNI字符串的处理
①获取字符串
JNI通过jstring来处理字符串数据,但是jstring是指向JVM内部的字符串,和C风格的字符串类型char * 不同,因此必须使用合适的JNI函数来访问JVM内部的字符串。
因为Java默认使用unicode编码,而C/C++默认使用UTF编码,所以要注意进行编码转换。
const char* GetStringUTFChars(jstring str, jbbolean *isCopy);
- str为需要获取的字符串
- isCopy取值JNI_TRUE和JNI_FALSE,一般填NULL即可
- JNI_TRUE:返回JVM内部源字符串的拷贝,并为新产生的字符串分配内存空间
- JNI_FALSE:返回JVM内部源字符串的指针,并可以指针修改源字符串的内容
②释放字符串
通过GetStringUTFChars获取到字符串并返回的为源字符串拷贝后,在使用完毕要记得释放内存。
void ReleaseStringUTFChars(jstring str, const char* utf);
- str为需要释放的字符串指针
- utf为字节编码
③创建字符串
jstring NewStringUTF(const char * bytes);
- bytes为C/C++的字符串数据源
④其他字符串处理API
- GetStringChars、ReleaseStringChars:用于获取/释放Unicode格式的字符串
- GetStringUTFLength、GetStringLength:用于获取UTF-8/Unicode格式的字符串长度
- GetStringCritical、ReleaseStringCritical:用于直接返回/释放源字符串的指针,获取这个指针会导致暂停GC线程,如果GC线程暂停时又被其他线程触发GC的话,会出现系统死锁的阻塞调用
- GetStringUTFRegion、GetStringRegion:用于获取UTF-8/Unicode格式字符指定范围内的内容,并会将源字符串复制到一个预先分配的缓冲区内