android jni 调用java_Android JNI开发系列(九)JNI调用Java的静态方法实例方法
JNI調用Java的靜態方法&實例方法
package org.professor.jni.bean;
import android.util.Log;
/**
* Created by peng on 2018/10/11.
*/
public class Person {
/*C/CPP 調用Java 靜態方法 */
public native void callJavaStaticMethod();
/*C/C++ 調用Java 實例方法 */
public native void callJavaInstanceMethod();
public static void setPersonInfo(String name, int age) { Log.i("PERSON", "name= " + name + "\\t age=" + age); }
public void setPersonMoney(float money) { Log.i("PERSON", "money= " + money);
}
}
上面寫了一個Java Bean類,里面定義了兩個Native方法,分別用來調用,該類的靜態方法和實例方法,實現在本地native方法里
JNI調用靜態方法
# include
# include
# include
//extern "C" // C 編譯器編譯我的代碼
JNIEXPORT void JNICALL Java_org_professor_jni_bean_Person_callJavaStaticMethod(JNIEnv *env, jobject instance) {
//1.獲取類類型的Class對象
jclass personClass = (*env)->FindClass(env, "org/professor/jni/Person");
if (NULL == personClass) {
__android_log_print(ANDROID_LOG_ERROR, "PERSON", "NOT FIND CLASS");
return;
}
//2.獲取方法ID 方法簽名:(String:Ljava/lang/String ;int:I) 方法簽名可以通過javap -s命令生成
jmethodID pJmethodID = (*env)->GetStaticMethodID(env, personClass, "setPersonInfo",
"(Ljava/lang/String;I)V");
if (NULL == pJmethodID) {
__android_log_print(ANDROID_LOG_ERROR, "PERSON", "NOT FIND STATIC METHOD");
return;
}
jstring str = (*env)->NewStringUTF(env, "Yimi");
//3.調用方法
(*env)->CallStaticVoidMethod(env, personClass, pJmethodID, str, 18);
// 刪除局部變量引用表,JVM內部由于引用表,
// 記錄全局和局部的變量,當超過引用表數量,導致內存溢出
// 造成崩潰
(*env)->DeleteLocalRef(env, personClass);
(*env)->DeleteLocalRef(env, str);
}
注意:
JVM 針對所有數據類型的返回值都定義了相關的函數。上面callStaticMethod 方法的返回類型為 void,所以調用 CallStaticVoidMethod。根據返回值類型不同,JNI 提供了一系列不同返回值的函數,如:CallStaticIntMethod、CallStaticFloatMethod、CallStaticShortMethod、CallStaticObjectMethod等,分別表示調用返回值為 int、float、short、Object 類型的函數,引用類型統一調用CallStaticObjectMethod 函數。另外,每種返回值類型的函數都提供了接收3種實參類型的實現:CallStaticXXXMethod(env, clazz, methodID, ...),CallStaticXXXMethodV(env, clazz, methodID, va_list args),CallStaticXXXMethodA(env, clazz, methodID, const jvalue args),分別表示:接收可變參數列表、接收 va_list 作為實參和接收const jvalue為實參。下面是jni.h頭文件中CallStaticVoidMethod 的三種實參的函數原型: void (JNICALL *CallStaticVoidMethod) (JNIEnv *env, jclass cls, jmethodID methodID, ...);
void (JNICALL *CallStaticVoidMethodV) (JNIEnv *env, jclass cls, jmethodID methodID, va_list args);
void (JNICALL *CallStaticVoidMethodA) (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args);
雖然函數結束后,JVM 會自動釋放所有局部引用變量所占的內存空間。但還是手動釋放一下比較安全,因為在 JVM 中維護著一個引用表,用于存儲局部和全局引用變量,經測試在 Android NDK 環境下,這個表的最大存儲空間是512 個引用,如果超過這個數就會造成引用表溢出,JVM 崩潰。在 PC 環境下測試,不管申請多少局部引用也不釋放都不會崩,我猜可能與 JVM 和 Android Dalvik 虛擬機實現方式不一樣的原因。所以有申請就及時釋放是一個好的習慣!(局部引用和全局引用在后面的文章中會詳細介紹)
JNI調用實例方法
JNIEXPORT void JNICALL Java_org_professor_jni_bean_Person_callJavaInstanceMethod(JNIEnv *env, jobject instance) {
//1.獲取類類型的Class對象
jclass personClass = (*env)->FindClass(env, "org/professor/jni/Person");
if (NULL == personClass) {
__android_log_print(ANDROID_LOG_ERROR, "PERSON", "NOT FIND CLASS");
return;
}
//2.獲取方法ID 方法簽名:(String:Ljava/lang/String ;int:I)
jmethodID pJmethodID = (*env)->GetMethodID(env, personClass, "setPersonMoney",
"(F)V");
if (NULL == pJmethodID) {
__android_log_print(ANDROID_LOG_ERROR, "PERSON", "NOT FIND INSTANCE METHOD");
return;
}
//3.獲取默認的構造方法ID (; = public org.professor.jni.bean.Person(); ),先獲取構造函數的ID
jmethodID constructor = (*env)->GetMethodID(env, personClass, "", "()V");
if (NULL == constructor) {
__android_log_print(ANDROID_LOG_ERROR, "PERSON", "NOT FIND INSTANCE CONSTRUCTOR METHOD");
return;
}
//4.獲取默認構造函數對象
jobject object = (*env)->NewObject(env, personClass, constructor);
if (NULL == object) {
__android_log_print(ANDROID_LOG_ERROR, "PERSON", "NOT FIND OBJECT");
return;
}
//5.調用實例方法
(*env)->CallVoidMethod(env, object, pJmethodID, 30.0);
//6.刪除局部引用變量
(*env)->DeleteLocalRef(env, personClass);
(*env)->DeleteLocalRef(env, object);
}
擴展方法簽名表
簽名
java類型
V
void
Z
boolean
I
int
J
long
D
double
F
float
B
byte
C
char
S
short
[I
int[]
[F
float[]
[B
byte[]
[C
char[]
[S
short[]
[D
double[]
[J
long[]
[Z
boolean[]
L用/分割包的完整類名; Ljava/lang/String;
Object
例如: void set(String str); 簽名:"(Ljava/lang/String;)V"
可以用javap -s "ClassFile" 命令查看方法簽名
總結
調用靜態方法使用 CallStaticXXXMethod/V/A 函數,XXX 代表返回值的數據類型。如:CallStaticIntMethod
調用實例方法使用 CallXXXMethod/V/A 函數,XXX 代表返回的數據類型,如:`CallIntMethod
獲取一個實例方法的 ID,使用 GetMethodID 函數,傳入方法名稱和方法簽名
獲以一個靜態方法的 ID,使用 GetStaticMethodID 函數,傳入方法名稱和方法簽名
獲取構造方法 ID,方法名稱使用""
獲取一個類的 Class 實例,使用 FindClass 函數,傳入類描述符。JVM 會從 classpath 目錄下開始搜索。
創建一個類的實例,使用 NewObject 函數,傳入 Class 引用和構造方法 ID
刪除局部變量引用,使用 DeleteLocalRef, 傳入引用變量
方法簽名格式:(形參參數列表)返回值類型。注意:形參參數列表之間不需要用空格或其它字符分隔
類描述符格式:L 包名路徑/類名;,包名之間用/分隔。如:Ljava/lang/String;
調用 GetMethodID 獲取方法 ID 和調用 FindClass 獲取 Class 實例后,要做異常判斷
總結
以上是生活随笔為你收集整理的android jni 调用java_Android JNI开发系列(九)JNI调用Java的静态方法实例方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 登录mysql报错2059_navica
- 下一篇: java postdelayed_And