java native interface JNI 調用Java方法
在上一篇文章中介紹了JNI。以及java調用JNI。這篇講一下 JNI調用java方法。
通過使用合適的JNI函數,你能夠創建Java對象,get、set 靜態(static)和 實例(instance)的域,調用靜態(static)和實例(instance)函數。
JNI通過ID識別域和方法。一個域或方法的ID是不論什麽處理域和方法的函數的必須參數。
下表列出了用以得到靜態(static)和實例(instance)的域與方法的JNI函數。每一個函數接受(作為參數)域或方法的類,它們的名稱,符號和它們相應返回的jfieldID或jmethodID。
函數 | 描寫敘述 |
GetFieldID |
得到一個實例的域的ID |
GetStaticFieldID | 得到一個靜態的域的ID |
GetMethodID | 得到一個實例的方法的ID |
GetStaticMethodID | 得到一個靜態方法的ID |
構造一個Java對象的實例
jmethodID id = (*env)->GetMethodID(env, cls, "", "(D)V"); //註意這裏方法的名稱是"",它表示這是一個構造函數。並且構造參數是double型的
jobject obj = (*env)->NewObjectA(env, cls, id, args); //獲得一實例,args是構造函數的參數,它是一個jvalue*類型。
首先是獲得一個Java類的class引用 (*env)->FindClass(env, "Lpackagename/classname;"); 請註意參數:Lpackagename/classname; 。L代表這是在描寫敘述一個對象類型,packagename/classname是該對象耳朵class路徑,請註意一定要以分號(;)結束!
然後是獲取函數的id。jmethodID id = env->GetMethodID(cls, "", "(D)V"); 第一個是剛剛獲得的class引用,第二個是方法的名稱,最後一個就是方法的簽名了
難理解的函數簽名
JNINativeMethod的定義例如以下:
jmethodID id = (*env)->GetMethodID(env, cls, "", "(D)V"); //註意這裏方法的名稱是"",它表示這是一個構造函數。 並且構造參數是double型的
jobject obj = (*env)->NewObjectA(env, cls, id, args); //獲得一實例。args是構造函數的參數,它是一個jvalue*類型。
第一個變量name是Java中函數的名字。
第二個變量signature,用字符串是描寫敘述了函數的參數和返回值
第三個變量fnPtr是函數指針。指向C函數。
當中比較難以理解的是第二個參數。比如
"()V"
"(II)V"
"(Ljava/lang/String;Ljava/lang/String;)V"
實際上這些字符是與函數的參數類型一一相應的。
"()" 中的字符表示參數。後面的則代表返回值。比如"()V" 就表示void Func();
"(II)V" 表示 void Func(int, int);
那其它情況呢?請查看下表:
類型 | 符號 |
boolean | Z |
byte | B |
char | C |
short | S |
int | I |
long | L |
float | F |
double | D |
void | V |
object對象 | LClassName; L類名; |
Arrays | [array-type [數組類型 |
methods方法 | (argument-types)return-type (參數類型)返回類型 |
稍稍補充一下:
1、方法參數或者返回值為java中的對象時,簽名中必須以“L”加上其路徑,只是此路徑必須以“/”分開。自己定義的對象也使用本規則
比方說 java.lang.String為“java/lang/String”,com.nedu.jni.helloword.Student為"Lcom /nedu/jni/helloword/Student;"
2、方法參數或者返回值為數組類型時,請前加上[
比如[I表示 int[],[[[D表示 double[][][]。即幾維數組就加幾個[
在本地方法中調用Java對象的方法
1、獲取你須要訪問的Java對象的類:
jclass cls = (*env)->GetObjectClass(env, obj); // 使用GetObjectClass方法獲取obj相應的jclass。jclass cls = (*env)->FindClass(“android/util/log”) // 直接搜索類名,須要是static修飾的類。
2、獲取MethodID:
jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V"); //GetStaticMethodID(…)。獲取靜態方法的ID使用GetMethdoID方法獲取你要使用的方法的MethdoID其參數的意義:
env-->JNIEnv
cls-->第一步獲取的jclass
"callback"-->要調用的方法名
"(I)V"-->方法的Signature, 簽名同前面的JNI規則。
3、調用方法:
(*env)->CallVoidMethod(env, obj, mid, depth);// CallStaticIntMethod(….) , 調用靜態方法
使用CallVoidMethod方法調用方法。參數的意義:
env-->JNIEnv
obj-->通過本地方法穿過來的jobject
mid-->要調用的MethodID(即第二步獲得的MethodID)
depth-->方法須要的參數(相應方法的需求,加入相應的參數)
註:這裏使用的是CallVoidMethod方法調用,由於沒有返回值,假設有返回值的話使用相應的方法。在後面會提到。
CallVoidMethod CallStaticVoidMethodCallIntMethod CallStaticVoidMethod
CallBooleanMethod CallStaticVoidMethod
CallByteMethod CallStaticVoidMethod
Jni操作Java的String對象
從java程序中傳過去的String對象在本地方法中相應的是jstring類型,jstring類型和c中的char*不同。所以假設你直接當做char*使用的話,就會出錯。
因此在使用之前須要將jstring轉換成為c/c++中的char*,這裏使用JNIEnv提供的方法轉換。
const char *str = (*env)->GetStringUTFChars(env, jstr, 0); (*env)->ReleaseStringUTFChars(env, jstr, str);
這裏使用GetStringUTFChars方法將傳進來的prompt(jstring類型)轉換成為UTF-8的格式,就行在本地方法中使用了。
註意:在使用完你所轉換之後的對象之後,須要顯示調用ReleaseStringUTFChars方法。讓JVM釋放轉換成UTF-8的string的對象的空間。假設不顯示的調用的話。JVM中會一直保存該對象,不會被垃圾回收器回收,因此就會導致內存溢出。
以下是Jni訪問String對象的一些方法:
- GetStringUTFChars 將jstring轉換成為UTF-8格式的char*
- GetStringChars 將jstring轉換成為Unicode格式的char*
- ReleaseStringUTFChars 釋放指向UTF-8格式的char*的指針
- ReleaseStringChars 釋放指向Unicode格式的char*的指針
- NewStringUTF 創建一個UTF-8格式的String對象
- NewString 創建一個Unicode格式的String對象
- GetStringUTFLength 獲取UTF-8格式的char*的長度
- GetStringLength 獲取Unicode格式的char*的長度
以下提供兩個String對象和char*互轉的方法:
/* c/c++ string turn to java jstring */jstring charToJstring(JNIEnv* env, const char* pat)
{
jclass strClass = (*env)->FindClass(env, "java/lang/String");
jmethodID ctorID = (*env)->GetMethodID(env, strClass, "", "([BLjava/lang/String;)V");
jbyteArray bytes = (*env)->NewByteArray(env, strlen(pat));
(*env)->SetByteArrayRegion(env, bytes, 0, strlen(pat), (jbyte*)pat);
jstring encoding = (*env)->NewStringUTF(env, "UTF-8");
return (jstring)(*env)->NewObject(env, strClass, ctorID, bytes, encoding);
}
/* java jstring turn to c/c++ char* */
char* jstringToChar(JNIEnv* env, jstring jstr)
{
char* pStr = NULL;
jclass jstrObj = (*env)->FindClass(env, "java/lang/String");
jstring encode = (*env)->NewStringUTF(env, "utf-8");
jmethodID methodId = (*env)->GetMethodID(env, jstrObj, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray byteArray = (jbyteArray)(*env)->CallObjectMethod(env, jstr, methodId, encode);
jsize strLen = (*env)->GetArrayLength(env, byteArray);
jbyte *jBuf = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE);
if (jBuf > 0)
{
pStr = (char*)malloc(strLen + 1);
if (!pStr)
{
return NULL;
}
memcpy(pStr, jBuf, strLen);
pStr[strLen] = 0;
}
env->ReleaseByteArrayElements(byteArray, jBuf, 0);
return pStr;
}
事實上在JNI中調用java的特性和在java中區別不大都是同樣思想,比方先找類。再找構造,再創建對象。然後就進行一系列操作...等等。
參考:http://zhiweiofli.iteye.com/blog/1830321
如有問題請留言,轉載註明出處。
java native interface JNI 調用Java方法