[JNI]開發之旅(8)傳遞引數給JNI函式
阿新 • • 發佈:2019-02-06
本節將介紹在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}]