Android jni/ndk编程三:native访问java
一.访问静态字段
Java层的field和method,不管它是public,还是package、private和protected,从
JNI都可以访问到,Java面向语言的封装性不见了。
静态字段和非静态的字段访问方式不同,jni规范提供了一系列带static标示的访问静态字段的函数:
jobject (*GetStaticObjectField)(JNIEnv*, jclass, jfieldID);
jboolean (*GetStaticBooleanField)(JNIEnv*, jclass, jfieldID);
jbyte (*GetStaticByteField)(JNIEnv*, jclass, jfieldID);
jchar (*GetStaticCharField)(JNIEnv*, jclass, jfieldID);
jshort (*GetStaticShortField)(JNIEnv*, jclass, jfieldID);
jint (*GetStaticIntField)(JNIEnv*, jclass, jfieldID);
jlong (*GetStaticLongField)(JNIEnv*, jclass, jfieldID);
jfloat (*GetStaticFloatField)(JNIEnv*, jclass, jfieldID) __NDK_FPABI__;
jdouble (*GetStaticDoubleField)(JNIEnv*, jclass, jfieldID) __NDK_FPABI__;
void (*SetStaticObjectField)(JNIEnv*, jclass, jfieldID, jobject);
void (*SetStaticBooleanField)(JNIEnv*, jclass, jfieldID, jboolean);
void (*SetStaticByteField)(JNIEnv*, jclass, jfieldID, jbyte);
void (*SetStaticCharField)(JNIEnv*, jclass, jfieldID, jchar);
void (*SetStaticShortField)(JNIEnv*, jclass, jfieldID, jshort);
void (*SetStaticIntField)(JNIEnv*, jclass, jfieldID, jint);
void (*SetStaticLongField)(JNIEnv*, jclass, jfieldID, jlong);
void (*SetStaticFloatField)(JNIEnv*, jclass, jfieldID, jfloat) __NDK_FPABI__;
void (*SetStaticDoubleField)(JNIEnv*, jclass, jfieldID, jdouble) __NDK_FPABI__;
访问流程:
- 获得java层的类:jclass cls = (*env)->GetObjectClass(env, obj);
- 获得字段的ID:jfieldID fid = (*env)->GetStaticFieldID(env, cls, “s”, “Ljava/lang/String;”);
- 获得字段的值:jstring jstr = (*env)->GetStaticObjectField(env, cls, fid);
- 设置字段的值:(*env)->SetStaticObjectField(env, cls, fid, jstr);
按照以上的流程,参照上面访问静态字段的函数定义,写如下测试代码:
void native_accessJava(JNIEnv * env, jobject obj){
LOGE("lstr:native_accessJava");
//1. 获得java层的类:jclass cls = (*env)->GetObjectClass(env, obj);
jclass cls = (*env)->GetObjectClass(env, obj);
//2. 获得字段的ID:jfieldID fid = (*env)->GetStaticFieldID(env, cls, "s", "Ljava/lang/String;");
jfieldID fid = (*env)->GetStaticFieldID(env, cls, "s", "Ljava/lang/String;");
if (fid == NULL) {
LOGE("get feild id error");
return; /* failed to find the field */
}
//3. 获得字段的值:jstring jstr = (*env)->GetStaticObjectField(env, cls, fid);
jstring jstr = (*env)->GetStaticObjectField(env, cls, fid);
LOGE("lstr:native_accessJava");
const char * lstr = (*env)->GetStringUTFChars(env,jstr,NULL);
LOGE("lstr: %s",lstr);
(*env)->ReleaseStringUTFChars(env,jstr,lstr);
//4. 设置字段的值:(*env)->SetStaticObjectField(env, cls, fid, jstr);
jstr = (*env)->NewStringUTF(env, "jni set");
if (jstr == NULL) {
return; /* out of memory */
}
(*env)->SetStaticObjectField(env, cls, fid, jstr);
}
注册方法的数组:
static JNINativeMethod gMethods[] = {
{"sayHello", "([I)I", (void *)native_sayHello},
{"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry},
{"accessJava","()V",(void *)native_accessJava},
};
java中访问的代码:
public class MainActivity extends AppCompatActivity {
TextView textView = null;
static String s = "java str";
static {
System.loadLibrary("hello");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text);
accessJava();
textView.setText(s);
}
public native int sayHello(int []arr);
public native String[] arrayTry(String [] arr);
public native void accessJava();
}
在jni代码所在目录执行adk-build命令,把编译生成的libhello.so文件拷贝到Android工程的jniLibs目录下,运行android程序即可看到现象。
二.访问实例字段
有了访问静态字段的经历,在去写访问实例字段的代码就简单多了,这里总结下使用流程:
- 获得java层的类:jclass cls = (*env)->GetObjectClass(env, obj);
- 获得字段的ID:jfieldID fid = (*env)->GetFieldID(env, cls, “ss”, “Ljava/lang/String;”);
- 获得字段的值:jstring jstr = (*env)->GetObjectField(env, obj, fid);
- 设置字段的值:(*env)->SetObjectField(env, obj, fid, jstr);
在写代码之前,先看一下jni.h中定义的访问实例字段的函数:
jfieldID (*GetFieldID)(JNIEnv*, jclass, const char*, const char*);
jobject (*GetObjectField)(JNIEnv*, jobject, jfieldID);
jboolean (*GetBooleanField)(JNIEnv*, jobject, jfieldID);
jbyte (*GetByteField)(JNIEnv*, jobject, jfieldID);
jchar (*GetCharField)(JNIEnv*, jobject, jfieldID);
jshort (*GetShortField)(JNIEnv*, jobject, jfieldID);
jint (*GetIntField)(JNIEnv*, jobject, jfieldID);
jlong (*GetLongField)(JNIEnv*, jobject, jfieldID);
jfloat (*GetFloatField)(JNIEnv*, jobject, jfieldID) __NDK_FPABI__;
jdouble (*GetDoubleField)(JNIEnv*, jobject, jfieldID) __NDK_FPABI__;
void (*SetObjectField)(JNIEnv*, jobject, jfieldID, jobject);
void (*SetBooleanField)(JNIEnv*, jobject, jfieldID, jboolean);
void (*SetByteField)(JNIEnv*, jobject, jfieldID, jbyte);
void (*SetCharField)(JNIEnv*, jobject, jfieldID, jchar);
void (*SetShortField)(JNIEnv*, jobject, jfieldID, jshort);
void (*SetIntField)(JNIEnv*, jobject, jfieldID, jint);
void (*SetLongField)(JNIEnv*, jobject, jfieldID, jlong);
void (*SetFloatField)(JNIEnv*, jobject, jfieldID, jfloat) __NDK_FPABI__;
void (*SetDoubleField)(JNIEnv*, jobject, jfieldID, jdouble) __NDK_FPABI__;
可以看到访问实例字段的函数和访问静态字段的函数在名字上就有区别,而且一定要注意的是,访问实例字段函数的第三个参数是jobject,是一个对象,而访问静态字段的第三个参数是jclass,是一个类。
我们使用上面给出的函数和我们总结的使用流程写如下代码:
void native_accessinstanceJava(JNIEnv * env, jobject obj){
LOGE("lstr:native_accessinstanceJava");
//1. 获得java层的类:jclass cls = (*env)->GetObjectClass(env, obj);
jclass cls = (*env)->GetObjectClass(env, obj);
//2. 获得字段的ID:jfieldID fid = (*env)->GetFieldID(env, cls, "ss", "Ljava/lang/String;");
jfieldID fid = (*env)->GetFieldID(env, cls, "ss", "Ljava/lang/String;");
if (fid == NULL) {
LOGE("get feild id error");
return; /* failed to find the field */
}
//3. 获得字段的值:jstring jstr = (*env)->GetObjectField(env, cls, fid);
jstring jstr = (*env)->GetObjectField(env, obj, fid);
const char * lstr = (*env)->GetStringUTFChars(env,jstr,NULL);
LOGE("lstr: %s",lstr);
(*env)->ReleaseStringUTFChars(env,jstr,lstr);
//4. 设置字段的值:(*env)->SetObjectField(env, cls, fid, jstr);
jstr = (*env)->NewStringUTF(env, "jni set");
if (jstr == NULL) {
return; /* out of memory */
}
(*env)->SetObjectField(env, obj, fid, jstr);
}
注册方法的数组:
static JNINativeMethod gMethods[] = {
{"sayHello", "([I)I", (void *)native_sayHello},
{"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry},
{"accessJava","()V",(void *)native_accessJava},
{"accessinstanceJava","()V",(void *)native_accessinstanceJava},
};
JNI_OnLoad等方法请参考之前的博客。
java层调用很非常简单,这里就不贴了。
三.访问静态方法
静态方法的访问总结为两步:
• 首先通过GetStaticMethodID在给定类中查找方法
如:jmethodID mid = (*env)->GetStaticMethodID(env,cls,”changeStr”,”()V”);
• 通过CallStaticMethod调用
如:(*env)->CallStaticVoidMethod(env, cls, mid);
jni中定义的访问静态方法的函数有如下一些:
jmethodID (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
jobject (*CallStaticObjectMethod)(JNIEnv*, jclass, jmethodID, ...);
jobject (*CallStaticObjectMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jobject (*CallStaticObjectMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
jboolean (*CallStaticBooleanMethod)(JNIEnv*, jclass, jmethodID, ...);
jboolean (*CallStaticBooleanMethodV)(JNIEnv*, jclass, jmethodID,va_list);
jboolean (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);
jbyte (*CallStaticByteMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jbyte (*CallStaticByteMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
jchar (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...);
jchar (*CallStaticCharMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jchar (*CallStaticCharMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
jshort (*CallStaticShortMethod)(JNIEnv*, jclass, jmethodID, ...);
jshort (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jshort (*CallStaticShortMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
jint (*CallStaticIntMethod)(JNIEnv*, jclass, jmethodID, ...);
jint (*CallStaticIntMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jint (*CallStaticIntMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
jlong (*CallStaticLongMethod)(JNIEnv*, jclass, jmethodID, ...);
jlong (*CallStaticLongMethodV)(JNIEnv*, jclass, jmethodID, va_list);
jlong (*CallStaticLongMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
jfloat (*CallStaticFloatMethod)(JNIEnv*, jclass, jmethodID, ...) __NDK_FPABI__;
jfloat (*CallStaticFloatMethodV)(JNIEnv*, jclass, jmethodID, va_list) __NDK_FPABI__;
jfloat (*CallStaticFloatMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) __NDK_FPABI__;
jdouble (*CallStaticDoubleMethod)(JNIEnv*, jclass, jmethodID, ...) __NDK_FPABI__;
jdouble (*CallStaticDoubleMethodV)(JNIEnv*, jclass, jmethodID, va_list) __NDK_FPABI__;
jdouble (*CallStaticDoubleMethodA)(JNIEnv*, jclass, jmethodID, jvalue*) __NDK_FPABI__;
void (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
void (*CallStaticVoidMethodV)(JNIEnv*, jclass, jmethodID, va_list);
void (*CallStaticVoidMethodA)(JNIEnv*, jclass, jmethodID, jvalue*);
结合上面总结的流程和jni.h中定义的函数,写如下测试代码:
代码功能:调用java层的静态方法,修改静态字段的值,把修改后的字段的值使用TextView显示出来。
void native_staticMethod(JNIEnv * env, jobject obj){
LOGE("native_staticMethod");
//1.获得类中方法id
jclass cls = (*env)->GetObjectClass(env, obj);
jmethodID mid = (*env)->GetStaticMethodID(env,cls,"changeStr","()V");
if (mid == NULL) {
LOGE("GetStaticMethodID error");
return; /* method not found */
}
LOGE("GetStaticMethodID sucess");
//2.调用CallStatic<ReturnValueType>Method函数调用对应函数.
(*env)->CallStaticVoidMethod(env, cls, mid);
}
注册方法的数组:
static JNINativeMethod gMethods[] = {
{"sayHello", "([I)I", (void *)native_sayHello},
{"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry},
{"accessJava","()V",(void *)native_accessJava},
{"accessinstanceJava","()V",(void *)native_accessinstanceJava},
{"staticMethod","()V",(void *)native_staticMethod},
};
添加了staticMethod方法的注册。
java层的调用:
public class MainActivity extends AppCompatActivity {
TextView textView = null;
static String s = "java str";
String ss = "instance str";
static {
System.loadLibrary("hello");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text);
staticMethod();
textView.setText(s);
}
public native int sayHello(int []arr);
public native String[] arrayTry(String [] arr);
public native void accessJava();
public native void accessinstanceJava();
public native void staticMethod();
public static void changeStr(){
s = "chang str";
}
}
四.访问实例方法
访问实例方法与访问静态方法类似,要注意的主要是:实例方法是属于对象jobject的,而静态方法是属于类的。
4.1普通实例方法
访问普通的实例方法的步骤还是总结为两步:
• 首先通过GetMethodID在给定类中查找方法
如:jmethodID mid = (*env)->GetMethodID(env,cls,”changeStr”,”()V”);
• 通过CallMethod调用
如:(*env)->CallStaticVoidMethod(env, obj, mid);
jni.h中定义的访问实例方法的相关函数有:
jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jobject (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
jobject (*CallObjectMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jobject (*CallObjectMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
jboolean (*CallBooleanMethod)(JNIEnv*, jobject, jmethodID, ...);
jboolean (*CallBooleanMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jboolean (*CallBooleanMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
jbyte (*CallByteMethod)(JNIEnv*, jobject, jmethodID, ...);
jbyte (*CallByteMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jbyte (*CallByteMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
jchar (*CallCharMethod)(JNIEnv*, jobject, jmethodID, ...);
jchar (*CallCharMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jchar (*CallCharMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
jshort (*CallShortMethod)(JNIEnv*, jobject, jmethodID, ...);
jshort (*CallShortMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jshort (*CallShortMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
jint (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
jint (*CallIntMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jint (*CallIntMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
jlong (*CallLongMethod)(JNIEnv*, jobject, jmethodID, ...);
jlong (*CallLongMethodV)(JNIEnv*, jobject, jmethodID, va_list);
jlong (*CallLongMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
jfloat (*CallFloatMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__;
jfloat (*CallFloatMethodV)(JNIEnv*, jobject, jmethodID, va_list) __NDK_FPABI__;
jfloat (*CallFloatMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) __NDK_FPABI__;
jdouble (*CallDoubleMethod)(JNIEnv*, jobject, jmethodID, ...) __NDK_FPABI__;
jdouble (*CallDoubleMethodV)(JNIEnv*, jobject, jmethodID, va_list) __NDK_FPABI__;
jdouble (*CallDoubleMethodA)(JNIEnv*, jobject, jmethodID, jvalue*) __NDK_FPABI__;
void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
void (*CallVoidMethodV)(JNIEnv*, jobject, jmethodID, va_list);
void (*CallVoidMethodA)(JNIEnv*, jobject, jmethodID, jvalue*);
jobject (*CallNonvirtualObjectMethod)(JNIEnv*, jobject, jclass,jmethodID, ...);
jobject (*CallNonvirtualObjectMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list);
jobject (*CallNonvirtualObjectMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*);
jboolean (*CallNonvirtualBooleanMethod)(JNIEnv*, jobject, jclass,jmethodID, ...);
jboolean (*CallNonvirtualBooleanMethodV)(JNIEnv*, jobject, jclass,jmethodID, va_list);
jboolean (*CallNonvirtualBooleanMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*);
jbyte (*CallNonvirtualByteMethod)(JNIEnv*, jobject, jclass, jmethodID, ...);
jbyte (*CallNonvirtualByteMethodV)(JNIEnv*, jobject, jclass,jmethodID, va_list);
jbyte (*CallNonvirtualByteMethodA)(JNIEnv*, jobject, jclass,jmethodID, jvalue*);
jchar (*CallNonvirtualCharMethod)(JNIEnv*, jobject, jclass, jmethodID, ...);
jchar (*CallNonvirtualCharMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list);
jchar (*CallNonvirtualCharMethodA)(JNIEnv*, jobject, jclass,jmethodID, jvalue*);
jshort (*CallNonvirtualShortMethod)(JNIEnv*, jobject, jclass,jmethodID, ...);
jshort (*CallNonvirtualShortMethodV)(JNIEnv*, jobject, jclass,jmethodID, va_list);
jshort (*CallNonvirtualShortMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*);
jint (*CallNonvirtualIntMethod)(JNIEnv*, jobject, jclass, jmethodID, ...);
jint (*CallNonvirtualIntMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list);
jint (*CallNonvirtualIntMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*);
jlong (*CallNonvirtualLongMethod)(JNIEnv*, jobject, jclass, jmethodID, ...);
jlong (*CallNonvirtualLongMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list);
jlong (*CallNonvirtualLongMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*);
jfloat (*CallNonvirtualFloatMethod)(JNIEnv*, jobject, jclass, jmethodID, ...) __NDK_FPABI__;
jfloat (*CallNonvirtualFloatMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list) __NDK_FPABI__;
jfloat (*CallNonvirtualFloatMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*) __NDK_FPABI__;
jdouble (*CallNonvirtualDoubleMethod)(JNIEnv*, jobject, jclass,jmethodID, ...) __NDK_FPABI__;
jdouble (*CallNonvirtualDoubleMethodV)(JNIEnv*, jobject, jclass,jmethodID, va_list) __NDK_FPABI__;
jdouble (*CallNonvirtualDoubleMethodA)(JNIEnv*, jobject, jclass, jmethodID, jvalue*) __NDK_FPABI__;
void (*CallNonvirtualVoidMethod)(JNIEnv*, jobject, jclass, jmethodID, ...);
void (*CallNonvirtualVoidMethodV)(JNIEnv*, jobject, jclass, jmethodID, va_list);
void (*CallNonvirtualVoidMethodA)(JNIEnv*, jobject, jclass,jmethodID, jvalue*);
4.2被子类覆盖的父类方法
我们看到了很多Nonvirtual方法,这是jni提供用来访问被子类赋给的父类的方法的,使用步骤如下:
调用被子类覆盖的父类方法: JNI支持用CallNonvirtualMethod满足这类需求:
• GetMethodID获得method ID
• 调用CallNonvirtualVoidMethod, CallNonvirtualBooleanMethod
上述,等价于如下Java语言的方式:
super.f();
CallNonvirtualVoidMethod可以调用构造函数
4.3构造函数
你可以像调用实例方法一样,调用构造方法,只是此时构造函数的名称叫做””.
综合上面三个知识点,我们设计如下代码时间这些知识:
1.在c函数中调用String类的构造函数新建一个字符串对象。
2.调用java的实例方法,传入我们1中构建的字符串对象,修改TextView的内容。
代码如下:
void native_instanceMethod(JNIEnv * env, jobject obj){
LOGE("native_instanceMethod");
//1.使用String类的构造函数构造String
//1.1找到String类
jclass stringClass = (*env)->FindClass(env, "java/lang/String");
if (stringClass == NULL) {
LOGE("FindClass error");
return;
}
//1.2找到String类的构造函数
jmethodID cid = (*env)->GetMethodID(env, stringClass, "<init>", "([C)V");
if (cid == NULL) {
LOGE("GetMethodID <init> error");
return; /* exception thrown */
}
//1.3创建字符数组
jint len = 10;
jcharArray elemArr = (*env)->NewCharArray(env, len);
if (elemArr == NULL) {
LOGE("NewCharArray error");
return; /* exception thrown */
}
jchar java_char[]={97,98,99,100,101,102,103,104,105,106};//abcdefghij
//1.4设置字符数组
(*env)->SetCharArrayRegion(env, elemArr, 0, len, java_char);
//1.5 创建一个字符串对象
jstring result = (*env)->NewObject(env, stringClass, cid, elemArr);
//2.获得类中方法id
jclass cls = (*env)->GetObjectClass(env, obj);
jmethodID mid = (*env)->GetMethodID(env,cls,"changeTextView","(Ljava/lang/String;)V");
if (mid == NULL) {
LOGE("GetMethodID error");
return; /* method not found */
}
LOGE("GetMethodID sucess");
//2.调用Call<ReturnValueType>Method函数调用对应函数.
(*env)->CallVoidMethod(env, obj, mid,result);
}
注册方法的数组:
static JNINativeMethod gMethods[] = {
{"sayHello", "([I)I", (void *)native_sayHello},
{"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry},
{"accessJava","()V",(void *)native_accessJava},
{"accessinstanceJava","()V",(void *)native_accessinstanceJava},
{"staticMethod","()V",(void *)native_staticMethod},
{"instanceMethod","()V",(void *)native_instanceMethod},
};
java层调用:
static JNINativeMethod gMethods[] = {
{"sayHello", "([I)I", (void *)native_sayHello},
{"arrayTry","([Ljava/lang/String;)[Ljava/lang/String;",(void *)native_arrayTry},
{"accessJava","()V",(void *)native_accessJava},
{"accessinstanceJava","()V",(void *)native_accessinstanceJava},
{"staticMethod","()V",(void *)native_staticMethod},
{"instanceMethod","()V",(void *)native_instanceMethod},
};
访问实例方法的实验到此结束。
五.性能与优化
5.1缓存Field 和 Method IDs
每次获得Field和Method IDS都比较耗时,如果我们需要多次获取他们,那就应该把它们缓存起来,这样以后用的时候就可以直接用了,从而节约了时间。
缓存的方式可以使用局部static字段缓存,也可以在类的初始化时,一次性缓存好全部的Field 和 Method IDs。
上述第一次使用缓存的方式,每次都有与NULL的判断,并且可能有一个无害的竞争条件。
而初始化类时,同时初始化JNI层对该类成员的缓存,可以弥补上述缺憾。
5.2影响jni回调性能的因素
首先比较Java/native和Java/Java
前者因下述原因可能会比后者慢:
• Java/native与Java/Java的调用约定不同. 所以,VM必须在调用前,对参数和调用
栈做特殊准备
• 常用的优化技术是内联. 相比Java/Java调用,Java/native创建内联方法很难
粗略估计:执行一个Java/native调用要比Java/Java调用慢2-3倍. 也可能有一些VM实
现,Java/native调用性能与Java/Java相当。(此种虚拟机,Java/native使用Java/Java
相同的调用约定)。
其次比较native/Java与Java/Java
native/Java调用效率可能与Java/Java有10倍的差距,因为VM一般不会做Callback的
优化。