1. 程式人生 > >[JNI]開發之旅(8)傳遞引數給JNI函式

[JNI]開發之旅(8)傳遞引數給JNI函式

本節將介紹在JNI程式設計中如何傳遞引數和返回值。

首先要強調的是,native方法不但可以傳遞Java的基本型別做引數,還可以傳遞更復雜的型別,比如String,陣列,甚至自定義的類。jni.h中定義了很多介面供我們操作。

其實在前面章節的例子中,我們已經使用到很多java傳遞引數給jni的例子,只是沒有重點介紹,接下來會對傳遞幾種基本型別引數,string,陣列,java自定義物件這些引數介紹。

1. 基本型別的傳遞

例項1:傳遞int型別給JNI函式

java程式碼:


    public native int intMethod(int n);

JNI實現程式碼:


//傳遞int引數
extern "C"
jint Java_com_honjane_ndkdemo_JNIUtils_intMethod( JNIEnv* env, jobject jobj,jint num){

    return num*num;
}

注意:num沒有經過轉換,直接使用的。對的8中基本資料型別傳遞都可以直接使用,不需要再轉換,而string,陣列,物件就不能直接使用了,需要把jni資料結構轉換成c,c++的資料結構才能操作。

結果:
傳遞num = 4;

I/main----intMethod: 16

例項2:傳遞boolean型別給JNI函式

java層:

    public native boolean booleanMethod(boolean bool);

JNI實現:


//傳遞布林型別
extern "C"
jboolean Java_com_honjane_ndkdemo_JNIUtils_booleanMethod(JNIEnv* env,jobject jobj,jboolean flag){

    return !flag;
}

結果:
flag == true

I/main----booleanMethod: false

這裡就介紹2個基本資料型別傳參,其他幾個型別,可以自行去實現。

2. String引數的傳遞

Java的String和C++的string是不能對等起來的,不能直接使用,需要通過一些函式轉換。

例項3:傳遞String給JNI函式,把引數拼接到jni字元上,然後給java層使用

java程式碼:

    public native String stringMethod(String text);

JNI實現:

//傳遞string型別
extern "C"
jstring Java_com_honjane_ndkdemo_JNIUtils_stringMethod(JNIEnv* env,jobject jobj,jstring jstr){
    //將jstring型別轉換成c++識別的char型別
    const char * str = env->GetStringUTFChars(jstr,0);
    char c[120] = "lily ";
    //呼叫c++拼接字元函式
    strcat(c,str);
    //釋放
    env->ReleaseStringUTFChars(jstr,str);
    return env->NewStringUTF(c);
}

我們看到與例項1,2兩個不一樣,拿到jstring後,通過GetStringUTFChars把java層傳過來的引數轉換成char指標,然後才能操作。

結果:
text:hello

I/main----stringMethod: lily hello

3. 陣列引數的傳遞

例項4:傳遞一個int陣列給jni,然後實現數字元素求和

java程式碼:

    public native int intArrayMethod(int[] intArray);

JNI實現:


extern "C"
jint Java_com_honjane_ndkdemo_JNIUtils_intArrayMethod(JNIEnv* env,jobject jobj,jintArray jarr){
     jint len=0,sum=0;
     //獲得陣列長度
     len = env->GetArrayLength(jarr);
     //轉換陣列為int指標
     jint* body = env->GetIntArrayElements(jarr,0);
     //由於一些版本不相容,i不定義在for迴圈中
     jint i=0;
     for(;i<len;i++){
        sum+=body[i];
     }
    return sum;
}

結果:
arr = {3,5,6,12,46,33};

I/main----intArrayMethod: 105

4. 自定義java物件引數的傳遞

例項5:傳遞複雜物件person,再jni函式中新構造一個person傳回java層輸出

定義Person

public class Person {
    private String name;
    private int age;


    public Person() {
    }

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Person :{ name: "+name+",age: "+age+"}";
    }


}

java層native函式

    public native Person objectMethod(Person person);

JNI實現:


//傳遞person自定義物件給jni函式
extern "C"
jobject  Java_com_honjane_ndkdemo_JNIUtils_objectMethod(JNIEnv* env,jobject jobj,jobject jperson){
    jclass jcls = env->GetObjectClass(jperson);
//    jclass jcls =  env->FindClass("com/honjane/ndkdemo/model/Person"); //或反射得Person類引用

    if(jcls == NULL){
        return env->NewStringUTF("not find class");
    }
    //得到person物件的建構函式Person(int,string)
    jmethodID constrocMID = env->GetMethodID(jcls,"<init>","(ILjava/lang/String;)V");
    if(constrocMID == NULL){
        return env->NewStringUTF("not find constroc method");
    }

    jstring str = env->NewStringUTF("honjane");
     //構造一個物件,呼叫該類的建構函式,並且傳遞引數
    jobject new_ojb = env->NewObject(jcls,constrocMID,21,str); 

 return new_ojb;
}

輸出結果:

I/main----objectMethod: Person :{ name:honjane ,age:21}

5. 集合引數的傳遞

例項6:傳遞複雜物件ArrayList,再jni函式中新構造一個ArrayList傳回java層輸出

java層

    public native ArrayList<Person> personArrayListMethod(ArrayList<Person> persons);

JNI實現:


//傳遞ArrayList<Person>集合給jni函式
extern "C"
jobject  Java_com_honjane_ndkdemo_JNIUtils_personArrayListMethod(JNIEnv* env,jobject jobj, jobject jlist){
    jclass jcls = env->GetObjectClass(jlist);
    if(jcls == NULL){
        return env->NewStringUTF("not find class");
    }
    jmethodID constrocMID = env->GetMethodID(jcls,"<init>","()V");
    if(constrocMID == NULL){
        return env->NewStringUTF("not find constroc method");
    }
    //建立一個Arraylist集合物件
    jobject list_obj = env->NewObject(jcls,constrocMID);
    //獲取list的add方法id
    jmethodID list_add  = env->GetMethodID(jcls,"add","(Ljava/lang/Object;)Z");


    jclass jpersonCls = env->FindClass("com/honjane/ndkdemo/model/Person");

    jmethodID jpersonConstrocMID = env->GetMethodID(jpersonCls,"<init>","(ILjava/lang/String;)V");

   for(int i = 0 ; i < 3 ; i++)
       {
           jstring str = env->NewStringUTF("Native");
           //通過呼叫該物件的建構函式來new 一個 person例項
           jobject per_obj = env->NewObject(jpersonCls , jpersonConstrocMID , 20+i ,str);  //構造一個person物件
           //執行Arraylist類例項的add方法,新增一個person物件
           env->CallBooleanMethod(list_obj ,list_add, per_obj);
       }

 return list_obj;
}

其中構造器是通過init+一對尖括號表示,這句程式碼的意思是:獲取Person(int,String)構造器的methodID


 env->GetMethodID(jpersonCls,"<init>","(ILjava/lang/String;)V");

輸出結果:

    I/main----輸出java list: [Person :{ name: lily,age: 10}, Person :{ name: lily,age: 11}, Person :{ name: lily,age: 12}]
    I/main----輸出jni list: [Person :{ name: Native,age: 20}, Person :{ name: Native,age: 21}, Person :{ name: Native,age: 22}]