1,在jni C/C++中調用java代碼的流程:
1)獲取類
2)調用靜態方法時,先獲取methodID(使用GetStaticMethodID),然後使用類似java invoke的形式將類和methodID作爲參數調用CallStaticMethod方法,即實現對java方法的調用。
3)調用非靜態方法時,還需要類的實例object,相關的方法有GetMethodID、Call***Method
2,JNIEnv類中有如下幾個簡單的函數可以取得類(jclass)
1)jclass FindClass(const char* clsName) 根據類名來查找一個類,完整類名。
2)jclass GetObjectClass(jobject obj) 根據一個對象,獲取該對象的類
3)jclass GetSuperClass(jclass obj) 獲取一個類的父類
FindClass 會在classpath系統環境變量下尋找類,需要傳入完整的類名,並注意包與包之間是用"/"而不是"."來分割
如:jclass cls_string= env->FindClass("java/lang/String");
以上相關函數都在jni.h定義。
3,使用GetMethodID獲取methodID前需要知道什麼?
1)方法簽名
使用javap命令來產生簽名:
javap -s -p [full class Name]
-s 表示輸出簽名信息
-p 同-private,輸出包括private訪問權限的成員信息
比如類TestNative的源碼:
package video1;
import java.util.Date;
public class TestNative {
public String name="Test";
public int number =100;
public int signTest(int i,Date date,int[] arr){
System.out.println("Sign Test");
return 0;
}
//native關鍵字修飾的方法,其內容是C/C++編寫的,java中不必爲它編寫具體的實現
public native void sayHello();
public static void main(String[] args) {
System.loadLibrary("NativeCode");
TestNative tn = new TestNative();
tn.sayHello();
}
}
使用javap命令獲取編輯後類的方法簽名
javap -s -private video1.TestNative
輸出:
Compiled from "TestNative.java"
public class video1.TestNative extends java.lang.Object{
public java.lang.String name;
Signature: Ljava/lang/String;
public video1.TestNative();
Signature: ()V
public int signTest(int, java.util.Date, int[]);
Signature: (ILjava/util/Date;[I)I
public native void sayHello();
Signature: ()V
public static void main(java.lang.String[]);
Signature: ([Ljava/lang/String;)V
}
2)java數據類型對應的簽名定義
類型 相應的簽名
boolean Z
byte B
char C
short S
int I
long J
float F
double D
void V
object L用/分隔包的完整類名: Ljava/lang/String;
Array [簽名 [I [Ljava/lang/Object;
Method (參數1類型簽名 參數2類型簽名···)返回值類型簽名
3)方法簽名舉例:
void f1() ()V
int f2(int, long) (IJ)I
boolean f3(int[]) ([I)Z
double f4(String, int) (Ljava/lang/String;I)D
void f5(int, String [], char) (I[Ljava/lang/String;C)V
特別注意:Object後面一定有分號(;)結束的,多個對象參數中間也用分號(;)來分隔
4)對於jmethodID GetMethodID(jclass clazz, const char *name, const char *sign)
clazz代表該屬性所在的類,name表示方法名稱,sign是簽名
(什麼是簽名,簽名是對函數參數和返回值的描述。對同一個函數,在java中允許重載,這個時候就需要這個sign來進行區分了)
舉例:
char *getAlgorithm(JNIEnv *env, jbyteArray jbyteArrays) {
//獲取類java.security.MessageDigest
jclass digest_clazz = (env)->FindClass("java/security/MessageDigest");
if (digest_clazz == NULL) {
return NULL;
}
//獲取方法 MessageDigest.getInstance(String algorithm)的ID,這個方法的返回值類型是MessageDigest
//如果類中沒有此方法,會返回NULL。但不報異常
jmethodID methodID_getInstance = (env)->GetStaticMethodID(digest_clazz, "getInstance", ""
"(Ljava/lang/String;)Ljava/security/MessageDigest;");
//調用方法 MessageDigest.getInstance(String algorithm),將字符串 "MD5" 作爲參數
jobject digest_object = (env)->CallStaticObjectMethod(digest_clazz, methodID_getInstance,
env->NewStringUTF("MD5"));
//獲取方法 void update(byte[] bytes)的ID
jmethodID methodID_update = (env)->GetMethodID(digest_clazz, "update", "([B)V");
//調用上述方法,jbyteArrays作爲字節數組類型的參數
(env)->CallVoidMethod(digest_object, methodID_update, jbyteArrays);
//獲取方法 byte[] digest()的ID
jmethodID methodID_digest = (env)->GetMethodID(digest_clazz, "digest", "()[B");
//調用上述方法
jbyteArray digestArray = (jbyteArray) (env)->CallObjectMethod(digest_object, methodID_digest);
jbyte *array = (env)->GetByteArrayElements(digestArray, NULL);
jsize len = (env)->GetArrayLength(digestArray);
unsigned char *encrypt = (unsigned char *) calloc((size_t) len + 1, sizeof(unsigned char));
for (int j = 0; j < len; ++j) {
encrypt[j] = (unsigned char) array[j];
}
encrypt[len] = '\0';
(env)->ReleaseByteArrayElements(digestArray, array, 0);
return getHexChar(encrypt, len);
}
參考:
http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/jniTOC.html
https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html
https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html