[转]JNI:使用RegisterNatives方法传递和使用Java自定义类
Posted Crysaty
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[转]JNI:使用RegisterNatives方法传递和使用Java自定义类相关的知识,希望对你有一定的参考价值。
JNI:使用RegisterNatives方法传递和使用Java自定义类
2012-03-04 13:30:22| 分类: Java | 标签:jni;registernatives |举报|字号 订阅
1、C++中函数命名自由,不必像javah自动生成的函数声明那样,拘泥特定的命名方式;
2、效率高。传统方式下,Java类call本地函数时,通常是依靠VM去动态寻找.so中的本地函数(因此它们才需要特定规则的命名格式),而使用RegisterNatives将本地函数向VM进行登记,可以让其更有效率的找到函数;
3、运行时动态调整本地函数与Java函数值之间的映射关系,只需要多次call RegisterNatives()方法,并传入不同的映射表参数即可。
为了使用RegisterNatives,我们需要了解JNI_OnLoad和JNI_OnUnload函数。JNI_OnLoad()函数在VM执行System.loadLibrary(xxx)函数时被调用,它有两个重要的作用:
- 指定JNI版本:告诉VM该组件使用那一个JNI版本(若未提供JNI_OnLoad()函数,VM会默认该使用最老的JNI 1.1版),如果要使用新版本的JNI,例如JNI 1.4版,则必须由JNI_OnLoad()函数返回常量JNI_VERSION_1_4(该常量定义在jni.h中) 来告知VM。
- 初始化设定,当VM执行到System.loadLibrary()函数时,会立即先呼叫JNI_OnLoad()方法,因此在该方法中进行各种资源的初始化操作最为恰当,RegisterNatives也在这里进行。
JNI_OnUnload()当VM释放该组件时被调用,JNI_OnUnload()函数的作用与JNI_OnLoad()对应,因此在该方法中进行善后清理,资源释放的动作最为合适。
Java代码和使用哪种方式实现JNI无关,如下所示:
class MyJavaClass
{
public int iValue;
public void Squa(){iValue = iValue*iValue;}
}
public class RegisterNativesTest
{
static{
System.load("/home/zmh/workspace/RegisterNativesTest/lib/libCallClass.so");
}
public static void main(String[] args)
{
RegisterNativesTest app = new RegisterNativesTest();
MyJavaClass obj = new MyJavaClass();
obj.iValue = 10;
System.out.println("Before callCustomClass: " + obj.iValue);
app.callCustomClass(obj);
System.out.println("After callCustomClass: " + obj.iValue);
}
private native void callCustomClass(MyJavaClass obj);
}
实现callCustomClass方法的代码如下:
void callCustomClass(JNIEnv* env, jobject, jobject obj)
{
jclass cls = env->GetObjectClass(obj);
jfieldID fid = env->GetFieldID(cls, "iValue", "I");
jmethodID mid = env->GetMethodID(cls, "Squa", "()V");
int value = env->GetIntField(obj, fid);
printf("Native: %d\n", value);
env->SetIntField(obj, fid, 5);
env->CallVoidMethod(obj, mid);
value = env->GetIntField(obj, fid);
printf("Native:%d\n", value);
}
注册callCustomClass在JNI_OnLoad中进行,代码如下:
static JNINativeMethod s_methods[] = {
{"callCustomClass", "(LMyJavaClass;)V", (void*)callCustomClass},
};
int JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
{
return JNI_ERR;
}
jclass cls = env->FindClass("LRegisterNativesTest;");
if (cls == NULL)
{
return JNI_ERR;
}
int len = sizeof(s_methods) / sizeof(s_methods[0]);
if (env->RegisterNatives(cls, s_methods, len) < 0)
{
return JNI_ERR;
}
return JNI_VERSION_1_4;
}
在C++和Java中创建关联的是JNINativeMethod,它在jni.h中定义:/*
* used in RegisterNatives to describe native method name, signature,
* and function pointer.
*/
typedef struct {
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;
字符 Java类型 C/C++类型
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
如果参数是Java类,则以"L"开头,以";"结尾,中间是用"/"隔开包及类名,例如Ljava/lang/String;,而其对应的C++函数的参数为jobject,一个例外是String类,它对应C++类型jstring。
以上是关于[转]JNI:使用RegisterNatives方法传递和使用Java自定义类的主要内容,如果未能解决你的问题,请参考以下文章