android JNI中JNIEnv類型和jobject類型的解釋


JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv * env, jobject obj)
{
cout<<"Hello World"<<endl;
}

對於這個方法參數中的JNIEnv* env參數的解釋:

JNIEnv類型實際上代表了Java環境,通過這個JNIEnv* 指針,就可以對Java端的代碼進行操作。例如,創建Java類中的對象,調用Java對象的方法,獲取Java對象中的屬性等等。JNIEnv的指針會被JNI傳入到本地方法的實現函數中來對Java端的代碼進行操作。

JNIEnv類中有很多函數可以用:

NewObject:創建Java類中的對象

NewString:創建Java類中的String對象

New<Type>Array:創建類型為Type的數組對象

Get<Type>Field:獲取類型為Type的字段

Set<Type>Field:設置類型為Type的字段的值

GetStatic<Type>Field:獲取類型為Type的static的字段

SetStatic<Type>Field:設置類型為Type的static的字段的值

Call<Type>Method:調用返回類型為Type的方法

CallStatic<Type>Method:調用返回值類型為Type的static方法

等許多的函數,具體的可以查看jni.h文件中的函數名稱。

 

參數:jobject obj的解釋:

如果native方法不是static的話,這個obj就代表這個native方法的類實例

如果native方法是static的話,這個obj就代表這個native方法的類的class對象實例(static方法不需要類實例的,所以就代表這個類的class對象)

 

下面來看一下Java和C++中的基本類型的映射關系:

為了能夠在C/C++中使用Java類,jni.h頭文件中專門定義了jclass類型來表示Java中的Class類

JNIEnv類中有如下幾個簡單的函數可以取得jclass:

 

jclass FindClass(const char* clsName):通過類的名稱(類的全名,這時候包名不是用.號,而是用/來區分的)來獲取jclass

如: jclass str = env->FindClass("java/lang/String");獲取Java中的String對象的class對象。

jclass GetObjectClass(jobject obj):通過對象實例來獲取jclass,相當於java中的getClass方法

jclass GetSuperClass(jclass obj):通過jclass可以獲取其父類的jclass對象

 

在C/C++本地代碼中訪問Java端的代碼,一個常見的應用就是獲取類的屬性和調用類的方法,為了在C/C++中表示屬性和方法,JNI在jni.h頭文件中定義了jfieldId,jmethodID類型來分別代表Java端的屬性和方法

我們在訪問,或者設置Java屬性的時候,首先就要先在本地代碼取得代表該Java屬性的jfieldID,然后才能在本地代碼中進行Java屬性操作,同樣的,我們需要呼叫Java端的方法時,也是需要取得代表該方法的jmethodID才能進行Java方法調用

 

使用JNIEnv的:

GetFieldID/GetMethodID

GetStaticFieldID/GetStaticMethodID

來取得相應的jfieldID和jmethodID

下面來具體看一下這幾個方法:

GetFieldID(jclass clazz,const char* name,const char* sign)

方法的參數說明:

clazz:這個簡單就是這個方法依賴的類對象的class對象

name:這個是這個字段的名稱

sign:這個是這個字段的簽名(我們知道每個變量,每個方法都是有簽名的)

 

GetMethodID也能夠取得構造函數的jmethodID,創建一個Java對象時可以調用指定的構造方法,這個將在后面向大家介紹:

 

如:env->GetMethodID(data_Class,"<init>","()V");

 

 

下面看一下簽名的格式:

通過例子來看一下這些方法的使用

package com.jni.demo;
public class JNIDemo {

public int number = 0;//定義一個屬性

//定義一個本地方法
public native void sayHello();
public static void main(String[] args){
//調用動態鏈接庫
System.loadLibrary("JNIDemo");
JNIDemo jniDemo = new JNIDemo();
jniDemo.sayHello();
System.out.print(jniDemo.number);
}
}

在來看一下C++代碼:

#include<iostream.h>
#include "com_jni_demo_JNIDemo.h"

JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv * env, jobject obj)
{
//獲取obj中對象的class對象
jclass clazz = env->GetObjectClass(obj);
//獲取Java中的number字段的id(最后一個參數是number的簽名)
jfieldID id_number = env->GetFieldID(clazz,"number","I");
//獲取number的值
jint number = env->GetIntField(obj,id_number);
//輸出到控制台
cout<<number<<endl;
//修改number的值為100,這里要注意的是jint對應C++是long類型,所以后面要加一個L
env->SetIntField(obj,id_number,100L);
}

編譯成功后,在Eclipse運行后的結果:

第一個0是在C++代碼中的cout<<number<<endl;

第二個100是在Java中的System.out.println(jniDemo.number);

 

JNIEnv提供了眾多的Call<Type>Method和CallStatic<Type>Method,還有CallNonvirtual<Type>Method函數,需要通過GetMethodID取得相應方法的jmethodID來傳入到上述函數的參數中

調用示例方法的三種形式:

Call<Type>Method(jobject obj,jmethodID id,....);

Call<Type>Method(jobject obj,jmethodID id,va_list lst);

Call<Type>Method(jobject obj,jmethodID id,jvalue* v);

第一種是最常用的方式

第二種是當調用這個函數的時候有一個指向參數表的va_list變量時使用的(很少使用)

第三種是當調用這個函數的時候有一個指向jvalue或jvalue數組的指針時用的

 

說明:

jvalue在jni.h頭文件中定義是一個union聯合體,在C/C++中,我們知道union是可以存放不同類型的值,但是當你給其中一個類型賦值之后,這個union就是這種類型了,比如你給jvalue中的s賦值的話,jvalue就變成了jshort類型了,所以我們可以定義一個jvalue數組(這樣就可以包含多種類型的參數了)傳遞到方法中。


假如現在Java中有這樣的一個方法:

boolean function(int a,double b,char c)

{

    ........

}

(1) 在C++中使用第一種方式調用function方法:

env->CallBooleanMethod(obj , id_function , 10L, 3.4 , L'a')

obj是方法funtion的對象

id_function是方法function的id;可以通過GetMethodID()方法獲取

然后就是對應的參數,這個和Java中的可變參數類似,對於最后一個char類型的參數L'a',為什么前面要加一個L,原因是Java中的字符時Unicode雙字節的,而C++中的字符時單字節的,所以要變成寬字符,前面加一個L

(2) 在C++中使用第三種法師調用function方法:

jvalue* args = new jvalue[3];//定義jvalue數組

args[0].i = 10L;//i是jvalue中的jint值

args[1].d = 3.44;

args[2].c = L'a';

env->CallBooleanMethod(obj, id_function, args);

delete[] args;//是否指針堆內存

例子:C++中調用Java中的方法:

Java代碼:

public double max(double value1,double value2){
return value1>value2 ? value1:value2;
}

 

JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv * env, jobject obj)
{
//獲取obj中對象的class對象
jclass clazz = env->GetObjectClass(obj);
//獲取Java中的max方法的id(最后一個參數是max方法的簽名)
jmethodID id_max = env->GetMethodID(clazz,"max","(DD)D");
//調用max方法
jdouble doubles = env->CallDoubleMethod(obj,id_max,1.2,3.4);
//輸出返回值
cout<<doubles<<endl;
}

 

C++和Java對於繼承后執行的是父類的還是子類的方法是有區別的,在Java中所有的方法都是virtual的,所以總是調用子類的方法,所以CallNonVirtual<Type>Method這個方法就出來了,這個方法就可以幫助我們調用Java中的父類的方法:

在JNI中定義的CallNonvirtual<Type>Method就能夠實現子類對象調用父類方法的功能,如果想要調用一個對象的父類方法,而不是子類的方法的話,就可以使用CallNonvirtual<Type>Method了,要使用它,首先要獲得父類及其要調用的父類方法的jmethodID,然后傳入到這個函數就能通過子類對象調用被覆寫的父類的方法了

例子:在Java中定義Father類:

 

package com.jni.demo;

public class Father {

public void function(){
System.out.println("Father:function");
}

}

在定義一個子類Child:繼承Father類,從寫父類中的function方法

package com.jni.demo;

public class Child extends Father{

@Override
public void function(){
System.out.println("Child:function");
}

}

在JNIDemo代碼:定義Father類型的屬性

package com.jni.demo;
public class JNIDemo {
public Father father = new Child();
//定義一個本地方法
public native void sayHello();
public static void main(String[] args){
//調用動態鏈接庫
System.loadLibrary("JNIDemo");
JNIDemo jniDemo = new JNIDemo();
jniDemo.sayHello();
}

}

在來看一下C++中的代碼:

#include<iostream.h>
#include "com_jni_demo_JNIDemo.h"

JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv * env, jobject obj)
{
//獲取obj中對象的class對象
jclass clazz = env->GetObjectClass(obj);
//獲取Java中的father字段的id(最后一個參數是father字段的簽名)
jfieldID id_father = env->GetFieldID(clazz,"father","Lcom/jni/demo/Father;");
//獲取father字段的對象類型
jobject father = env->GetObjectField(obj,id_father);
//獲取father對象的class對象
jclass clazz_father = env->FindClass("com/jni/demo/Father");
//獲取father對象中的function方法的id
jmethodID id_father_function = env->GetMethodID(clazz_father,"function","()V");
//調用父類中的function方法(但是會執行子類的方法)
env->CallVoidMethod(father,id_father_function);
//調用父類中的function方法(執行就是父類中的function方法)
env->CallNonvirtualVoidMethod(father,clazz_father,id_father_function);

}

Child:function是調用env->CallVoidMethod(...)方法的

Father:function是調用env->CallNonvirtualMethod(...)方法的

這樣就能夠控制到底調用哪個類的function方法了。

 

轉載自:http://blog.csdn.net/jiangwei0910410003/article/details/17466369

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM