Demo
//增加學生資訊:
addStu(JNIEnv *env,jobject valObj){
//擷取jclass對象
jclass cls=env->GetObjectClass(valObj);
//GetFieldID方法用到取到jclas中的name字段。參數清單(jclass對象,字段名稱,字段類型) 這點類似于java的反射
jstring name =(jstring)env->GetObjectField(valObj,env->GetFieldID(cls,"name","Ljava/lang/String;"));
jint age=(jint)env->GetObjectField(valObj,env->GetFieldID(cls,"age","I"));
jint sex =(jint)env->GetObjectField(valObj,env->GetFieldID(cls,"sex","I"));
//建立一個結構類型的對象 jstringToString方法用于把jstring類型轉換成char *
student stu={jstringToString(env,name),(int)age,(int)sex};
//往向量的末尾增加一個對象
stus.push_back(stu);
}
//修改學生資訊
upStu(JNIEnv *env,jobject obj,jobject objValue){
jclass cls=env->GetObjectClass(objValue);
jstring name=(jstring)env->GetObjectField(objValue,env->GetFieldID(cls,"name","Ljava/lang/String;"));
jint sex =(jint)env->GetObjectField(objValue,env->GetFieldID(cls,"sex","I"));
jint age=(jint)env->GetObjectField(objValue,env->GetFieldID(cls,"age","I"));
char * searchName =jstringToString(env,name);
for(int i=;i<stus.size();i++){
student stu=stus.at(i);
if(strcmp(stu.name,searchName)==){
stus.at(i).sex=(int)sex;
stus.at(i).age=(int)age;
}
}
}
//查詢學生
getStu(JNIEnv *env,jobject obj,jstring str){
const char *nameStr=env->GetStringUTFChars(str,);
jclass objectClass =(env)->FindClass("com/myjni/activity/Student");
jfieldID name =env->GetFieldID(objectClass,"name","Ljava/lang/String;");
jfieldID sex =env->GetFieldID(objectClass,"sex","I");
jfieldID age =env->GetFieldID(objectClass,"age","I");
for(int i=;i<stus.size();i++){
student stu=stus.at(i);
if(strcmp(stu.name,nameStr)==){
env->SetObjectField(obj,env->NewStringUTF(stus.at(i).name));
env->SetIntField(obj,sex,stus.at(i).sex);
env->SetIntField(obj,age,stus.at(i).age);
}
}
return obj;
}
//擷取所有學生資訊
getStus(JNIEnv *env,jobject obj){
class objClass =(env)->FindClass("java/lang/Object");
jobjectArray args = ;
jsize len=stus.size();
args=env->NewObjectArray(len,objClass,);
jclass objectClass =(env)->FindClass("com/myjni/activity/Student");
jfieldID name=(env)->GetFieldID(objectClass,"name","Ljava/lang/String;");
jfieldID age=(env)->GetFieldID(objClass,"age","I");
jfieldID sex =(env)->GetFieldID(objClass,"sex","I");
for(int i=;i<len;i++){
jobject tempObj =env->AllocObject(env->GetObjectClass(obj));
student stu=stus.at(i);
env->SetObjectField(tempObj,name,env->NewStringUTF(stus.at(i).name));
env->SetObjectField(tempObj,age,stu.age);
env->SetObjectField(tempObj,sex,stu.sex);
env->SetObjectArrayElement(args,i,tempObj);
}
return args; //傳回的是數組對象
}
類型簽名:
Z boolean
B byte
C char
S short
I int
J long
F float
D double
Ljava/lang/String; String
[I int[]
資料類型
基本資料類型:
boolean byte char short int long float double --Java
jboolean jbyte jchar jshort jint jlong jfloat jdouble --jni
引用資料類型:
String Array[] Object --Java
String :
jstring是JNI對應于String的類型,但是和基本類型不同的是,jstring不能直接當做C++的string用。否則編譯器會扔給你一個錯誤資訊。
const char *str;
str =env->GetStringUTFChars(prompt,false); //先将java中的String轉換成chars
if(str==null){
return null;
}
cout<<str<<endl; //可以進行字元數組的輸出
env->ReleaseStringUTFChars(prompt,str); //注意釋放存儲,避免記憶體洩露
//如果要轉換成string對象
char *tmpstr="returnStringSucceeded";
jstring str =env->NewStringUTF(tmpstr); //将char轉換成jstring類型
總結:
将java中的String類型轉換成JNI中de jstring類型
const char *chars=env->GetStringUTFChars(prompt,false);
jstring str =env->NewStringUTF(chars);
數組類型:
JNI為Java基本類型的數組提供了j*Array類型,比如int[] 對應的就是jintArray
JNIEXPORT jint JNICALL Java_IntArray_sumArray(JNIEnv *env,jobject obj,jintArray arr){
jint *carr;
carr =env->GetIntArrayElements(arr,false);
if(carr==NULL){
return 0;
}
jintsum =0;
for(int i=0;i<10;i++){
sum+=carr;
}
env->ReleaseIntArrayElements(arr,carr,0);
return sum;
}
總結:
GetIntArrayElements和ReleaseIntArrayElements函數就死JNI提供用于處理int數組的函數。
二維數組和String數組:
在JNI中,二維數組和String數組都被視為object數組,因為數組和String被視為object。
JNIEXPORT jobjectArray JNICALL Java_ObjectArrayTest_initInt2DArray(JNIEnv *env,jclass cls,int size){
//因為要傳回值,是以需要建立一個jobjectArray對象
jobjectArray result;
//建立一個jclass的引用
jclass intArrCls = env->FindClass("[I");
//為result配置設定空間
result =env->NewObjectArray(size,intArrCls,NULL);
for(int i=0;i<size;i++){
jint tmp[256]; //保證存儲空間足夠大
//為一維int數組iarr配置設定空間
jintArray iarr=env->NewIntArray(size);
for(int j=0;j<size;j++){
tmp[j]=i+j;
}
//為iarr指派
env->SetIntArrayRegion(iarr,0,size,tmp);
//為result的第i個元素指派
env->StObjectArrayElements(result,i,iarr);
env->DeleteLocalRef(iarr);
}
return result;
}
//建立一個二維int數組,并指派完畢
注意:
以上使用的函數調用方式都是針對C++的,如果要用在C中,所有的env->都要被替換成(*env)->,而且後面的函數中需要增加一個參數env
通路Java類的域和方法 通路Java類的私有域和方法
public class ClassA{
String str="abcde";
int number;
public native void nativeMethod();
private void javaMethod(){
System.out.println("call javaMethod successed");
}
static{
System.loadLibrary("ClassA");
}
}
//C++實作
JNIEXPORT void JNICALL Java_testclass_ClassCallDLL_nariveMethod(JNIEnv *env,jobject obj){
//accessfield
jclass cls =env->GetObjectClass(obj); //得到對象
jfieldID fid=env->GetFieldID(cls,"str","Ljava/lang/String;");
jstring jstr =(jstring)env->GetObjectField(obj,fid);
const char *str=env->GetStringUTFChars(jstr,false);
if(std::string(str)=="abcde")
std::cout<<"accessfieldsuccesses"<<std::endl;
//得到字元字段
jint i=;
fid =env->GetFieldID(cls,"number","I");
env->SetIntField(obj,fid,i);
//設定值
jmethodID mid=env->GetMethodID(cls,"javaMethod","()V");
env->CallVoidMethod(obj,mid);
//調用不帶參數的方法
}
在native方法中使用使用者定義的類
使用自定義的類和使用Java的基礎類(比如String)沒有太大的差別,關鍵的一點是,如果要使用自定義類,首先要能通路類的構造函數。
jclass cls=env->FindClass("Ltestclass/ClassB"); //建立一個自定義類的引用
jmethodID mid=env->GetMethodID(cls,"<init>","(D)V"); //獲得這個類的構造函數
jdouble dd=;
jvalue args[];
args[].d=dd;
jobject obj=env->NewObjectA(cls,mid,args); //生成了一個ClassB的對象,args是ClassB的構造函數的參數,它是一個jvalue*類型。
異常處理
在native方法中發生了異常,傳導到Java中
jclass Cls;
env->ExceptionDescribe();
env->ExceptionClear();
errCls=env->FindClass("java/lang/IllegalArgumentException");
env->ThrowNew(errCls,"thrownfromC++code");
//如果要抛出其他類型的異常,替換掉FindClass的參數即可。這樣,在Java中就可以接收native方法中抛出的異常。
類的相關操作
jclass FindClass(JNIEnv *env,const char *name); //查找類
常見異常:
ClassFormatError 類的資料格式無效
ClassCircularityError 該類或接口是自身的超類或超接口
NoClassDefFoundError 沒有找到指定名稱的類或接口
OOM 記憶體不足錯誤,即OutOfMemoryError
jclass GetSuperclass(JNIEnv *env,jclass clazz); //擷取父類或者說超類
//第二個參數傳入的是子類,否則傳回将是NULL
jboolean IsAssignableFrom(JNIEnv *env,jclass clazz,jclass clazz2); //判斷class1對象能否安全的強制轉換為class2對象
/*
以下情況傳回true: JNI_TRUE
、這兩個類參數引用同一個Java類
、第一個類是第二個類的子類
、第二個類是第一個類的某個接口
jclass GetObjectClass(JNIEnv *env,jobject obj); //通過對象擷取這個類
//對象不能為NULL,否則擷取的class肯定傳回也為NULL
jboolean isInstanceOf(JNIEnv *env,jobject obj,jclass clazz); //判斷對象是否為某一個類的執行個體
/*
注意:
傳回值可能産生異議,就是如果傳入的第二個參數為NULL對象,NULL對象可以強制轉換為各種類,所有這種情況也将會傳回JNI_TRUE,是以一定判斷傳入的對象是否為空。
*/
jboolean IsSameObject(JNIEnv *env,jobject ref1,jobject ref2); //判斷兩個對象是否引用同一個類
/*
如果兩個對象均為空,傳回的值也會是JNI_TRUE是以使用時判斷對象為空
*/
調用Java方法:
jmethodID GetMethodID(JNIEnv *env,jclass clazz,const char *name,const char *sig); //擷取一個Java方法的ID
//這個函數将傳回非靜态類或接口執行個體方法的方法ID
/*
執行GetMethodID()函數将導緻未初始化的類初始化,如果要獲得構造函數的方法ID,使用<init>作為方法名,同時将void(V)作為傳回類型,如果找不到指定的ID将傳回NULL,同時異常可能有:
NoSuchMethodError 找不到指定的Java方法。
ExceptionInInitializerError 如果由于異常而導緻類初始化程式失敗
OutOfMemoryError 記憶體不足
*/
NativeType CallXXXMethod(JNIEnv *env,jobject obj,jmethodID methodID,va_list args); //調用XXX類型的Java方法
/*
執行Java類中的某個方法,需要注意的是這個裡的java類是非靜态的,由于Java的方法的類型比較多,是以該函數可能有以下幾種形式,如:CallObjectMethod,CallBooleanMethod,CallByteMetod,CallCharMethod,CallShortMethod,CallIntMethod和CallVoidMethod,需要注意的是,該函數的第三個參數為通過GetMethodID函數擷取的方法ID,最後一個參數為這個方法的參數表,最後的va_list宏可以通過搜尋擷取具體的使用方法。
*/
上面的三個均為非靜态類的擷取,執行調用,需要執行個體化這個類才可以執行,下面的為靜态調用。
jmethodID GetStatic MethodID(JNIEnv *env,jclass clazz,const char *name,const char *sig);
NativeType CallStatic XXXMethod(JNIEnv *env,jclass clazz,jmethodID methodID,...);
通路Java對象的域
jfieldID GetFieldID(JNIEnv *env,jclass clazz,const char *name,const char *sig); //擷取執行個體對象的域ID
/*
非靜态的執行個體化後的對象,可能産生的異常有:
NoSuchFieldError 找不到指定的域
ExceptionInInitializerError 因為異常而導緻類初始化失敗
OutOfMemoryError記憶體不足
*/
NativeType GetXXXField(JNIEnv *env, jobject obj,jfieldID fieldID);
//類似GetXXXMethod函數,可能有的類型有 GetObjectField,GetBooleanField,GetByteField,GetCharField,GetShortField,GetIntField,GetLongField
void SetXXXField(JNIEnv *env, jobject obj, jfieldID fieldID,NativeType value);
//Java的域可以指派的,可能有的類型有 SetObjectField,SetBooleanField,SetByteField,SetCharField,SetShortField,SetIntField,SetLongField
上面3種情況均為非靜态對象的域,對于不需要執行個體化對象的域,可以直接使用下面的
jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz,const char *name, const char *sig);
jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz,const char *name, const char *sig);
void SetStaticXXXField(JNIEnv *env, jclass clazz,jfieldID fieldID, NativeType value);