Jni C/C++開發基礎之基礎聚集(Android、Java範疇)

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

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章