jni编程可以在java端调用其他编程语言来为自己的应用服务,做java语言很难甚至做不到的事,功能强大可想而知。
java端数据类型 —> jni端数据类型
1. 常用数据类型
boolean ---> Z
int ---> I
float ---> F
double ---> D
long ---> J
byte ---> B
char ---> C
void ---> V
2. object数据类型
L开头,然后以/分隔类路径,后面再加;
例:String ---> Ljava/lang/String;
3. Array数据类型
以 [ 开头,跟着object数据类型签名
例: int[] ---> [I
int[][] ---> [[I
方法签名
(参数列表)返回值数据类型
例: public void set(int i1, int i2) ---> (II)V
获取jclass
- jclass jclass = env->FindClass("object完整类名");
- jclass jclass = env->GetObjectClass(jobject);
获取jmthodId
在jni端对java端对象的成员方法操作,必须要获取到这个方法的jmthodId
jmethodID methodId = env->GetMethodID(jclass, "方法名", "方法签名");//获取非静态方法
jmethodID methodId2 = env->GetStaticMethodID(jclass, "方法名", "方法签名");//获取静态方法
获取object构造方法签名:jmethodID methodIdCtr = env->GetStaticMethodID(jclass, "<init>", "方法签名");
jni端创建java端的对象
本质上是在java端创建对象,jni端持有一个对java端的引用,通过引用来操作这个对象。
jobject obj = env->NewObject(jclass, jmethodIdCtr,...);//可传入构造参数
jni获取java端成员变量
//非静态变量
jfieldID fieldId = env->GetFieldID(jclass, "变量名", "变量类型签名");
jobject field = env->GetObjectField(jobject, fieldId);
//静态变量
jfieldID staticFieldId = env->GetStaticFieldID(jclass, "变量名", "变量类型签名");
env->GetStaticObjectField(jclass, fieldId);
jni修改java端成员变量
//非静态变量
jfieldID fieldId = env->GetFieldID(jclass, "变量名", "变量类型签名");
env->SetObjectField(jobject, fieldId, value);
//静态变量
jfieldID staticFieldId = env->GetStaticFieldID(jclass, "变量名", "变量类型签名");
env->SetStaticObjectField(jclass, staticFieldId, value);
jni端调用java端成员方法
//非静态方法调用
jmethodID methodId = env->GetMethodID(jclass, "方法名", "方法签名");
env->CallIntMethod(jobject, methodId, ...);//传入参数列表
//静态方法调用
jmethodID methodId2 = env->GetStaticMethodID(jclass, "方法名", "方法签名");
env->CallStaticObjectMethod(jclass, methodId2, ...);
jni端数组的创建
//arrayLength: 数组大小
jobjectArray objectArray = env->NewObjectArray(arrayLength, jclass, NULL);
//为数组中元素赋值
env->SetObjectArrayElement(objectArray, index, value);
jni函数中也为一些简单数据类型数组提供创建方法:
env->NewIntArray(arrayLength);
env->NewBooleanArray(arrayLength);
...
...
jni端访问java端数组
jfieldID fieldId = env->GetFieldID(jclass, "变量名", "变量类型签名");
jobjectArray objectArray = env->GetObjectField(jobject, fieldId);
jobject object = env->GetObjectArrayElement(objectArray, index);
//修改元素
env->SetObjectArrayElement(objectArray, index, value);
如果想要缓存jclass,必须将其存在GlobalRef中
jclass classs = (jclass)env->NewGlobalRef(env->FindClass("object完整类名"));
globaleRef, localRef, weakGlobaleRef
JNI支持三种引用:局部引用、全局引用、弱全局引用。
① 局部引用
会阻止GC回收掉引用的VM对象;
会在本地方法返回时自动销毁,一般无需用户主动销毁。但是如果在for循环中创建局部引用,则需要手动销毁:
env->DeleteLocalRef(jobject);
因为局部引用会存放在一个引用表中,这个表存储的引用是有限的,创建大量的localRef而不及时销毁,引用表很可能会溢出。
② 全局引用
会阻止GC回收掉引用的VM对象;
必须手动调用:
env->DeleteGlobalRef(jobject);
销毁引用。
③ 弱全局引用
不会阻止GC回收掉引用的VM对象;
必须手动调用:
env->DeleteWeakGlobalRef(jobject);
销毁引用。
④ 比较两个引用是否引用了同一个VM对象
env->IsSameObject(ref, ref2);
JNI动态注册
#define JNIREG_CLASS "调用native方法的java类名"
//动态注册的方法
jstring cppGetStringFromJni(JNIEnv *env, jclass clazz)
{
char* str = "hello world from C \n";
return env->NewStringUTF(str);
}
//动态注册表
static JNINativeMethod cpp_g_methods[] = {
{ "java端声明的Native方法", "()Ljava/lang/String;", (void*)cppGetStringFromJni },
};
static bool cppRegisterNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* g_methods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
return false;
}
if (env->RegisterNatives(clazz, g_methods, numMethods) < 0) {
return false;
}
return true;
}
static bool cppRegisterNatives(JNIEnv* env)
{
if (!cppRegisterNativeMethods(env, JNIREG_CLASS, cpp_g_methods, sizeof(cpp_g_methods) / sizeof(cpp_g_methods[0])))
return false;
return true;
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
assert(env != NULL);
if (!cppRegisterNatives(env)) {
return -1;
}
result = JNI_VERSION_1_4;
return result;
}