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的
優化。
Android jni/ndk編程三:native訪問java