Android jni/ndk编程三:native访问java
来源:互联网 发布:java new object数组 编辑:程序博客网 时间:2024/05/16 01:38
上一篇博客我们总结了jni中的数据类型的转换,通过实战体验了如何转换基本类型、字符串、以及数组,数组又包括了原始类型数组和对象数组。这一篇博客将继续总结jni基础知识,并通过实际体验掌握jni编程。这篇博客将着重于本地代码访问java代码的相关知识。
本地代码访问java代码,主要是指访问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的
优化。
最后关于字段访问
对于field的访问,将没什么不同,只是通过JNI访问某对象结构中某个位置的值。
- Android jni/ndk编程三:native访问java
- Android JNI + NDK 编程
- android JNI native 编程
- android ndk jni层访问java对象小结
- Android NDK(JNI)学习总结一:Java代码中申明native函数-Java调用C函数,并在C函数中访问java类和方法、属性
- Android JNI NDK 编程学习
- Android Native jni 编程知识
- Android Native jni 编程入门
- Android NDK- native调用Java
- Android NDK 与 Java JNI
- 【学习Android NDK开发】native code通过JNI调用Java方法
- 【学习Android NDK开发】native code通过JNI调用Java方法
- android jni 编程 三
- JNI-Native代码访问Java代码(一)
- Android NDK(三):JNI 字符串
- Android Studio中JNI NDK开发(三)
- Android NDK开发三 JNI基础
- Java native方法及JNI实例(三)
- I帧、P帧、B帧
- 巴鲁赫·德·斯宾诺莎
- JavaBean,POJO,VO,DTO的区别和联系
- css3之使用选择器在页面中插入内容
- 软件设计师考试真题总结
- Android jni/ndk编程三:native访问java
- 微信小程序开发环境搭建
- iOS 音频拼接
- iOS10 去除打印日志
- 判断单链表是否有环的java实现
- JQuery对CheckBox的一些相关操作
- GitHub基础学习-找到GitHub某个远程仓库的访问地址
- 弗吉尼亚·伍尔芙
- 网络原理,以及对VMware Workstation虚拟网络VMnet0、VMnet1、VMnet8的图解