有時候我們的C++/C帶代碼是現成的,需要向上用JNI封裝,然後用Java調用。Java中是沒有指針的,但是Java通過JNI調用C++/C接口,C++/C是有指針的,這種情況Java該作何處理。
C++/C指針作爲輸出參數
這種在前面出參爲String類型的情況最後更爲一般的情況已經提到過。這裏在展開來討論。
基本類型
example:
Java接口:
public void getAge(int age[]);
JNI:
JNIEXPORT void JNICALL Java_setAge_ 1native (JNIEnv *env, jclass thiz,
jintArray age)
{
int age;
getAge(&age); // setAge爲更下層的函數接口
jint as32AgeArray[1];
s32AgeArray [0] = age;
/* 這個操作實際有些像memcpy,如果數組大小比較大,並且拷貝若干
個連續的元素,就修改上面方法的第2,3個參數即可
*/
env->SetIntArrayRegion(age, 0, 1, (jint *) as32AgeArray);
}
輸出參數類型是class
/* C++/C中 struct */
typedef struct _Para_tag
{
int x;
short y;
char az[100];
char h;
unsigned int t;
}Para_Tag;
對應的Java中的class爲:
public class Para_Tag
{
int m_x;
short m_y;
String m_z;
char m_h;
long m_t;
public Para_tag(int x,short y,String z,char h,long t)
{
m_x = x;
m_y = y;
m_z = z;
m_h = h;
m_t = t;
}
}
Java 接口:
public void getPara(Para_Tag tag);
JNI:
JNIEXPORT void JNICALL Java_getPara_ 1native (JNIEnv *env, jclass thiz, jobject obj)
{
Para_Tag tag;
getPara(&tag);// 取得C++/C中Para_Tag結構體的值
// 給java的Para_Tag class賦值
jclass DataCls = env->FindClass("com/Para_Tag"); // 通過class path找到class的標識
/* 對class中的每個變量逐一賦值 */
jfieldID dataId = env->GetFieldID(DataCls, "m_x", "I");
env->SetIntField(obj, dataId,tag.x);
dataId = env->GetFieldID(DataCls, "m_y", "S");
env->SetShortField(obj, dataId,tag.y);
/* 注意C++中的char * 類型如何通過JNI轉換爲Java的String */
dataId = env->GetFieldID(DataCls, "m_z", "Ljava/lang/String;");
jstring strTmp = env->NewStringUTF((char *)tag.az);
env->SetObjectField(objdata, dataId, strTmp);
dataId = env->GetFieldID(DataCls, "m_h", "C");
env->SetCharField(obj, dataId,tag.h);
dataId = env->GetFieldID(DataCls, "m_t", "J"); // 注意long對應的簽名是J,不是L
env->SetLongField(obj, dataId,tag.t);
}
上面這種做法是ok的,但是效率太低,每調用一次vm的env函數就會和虛擬機交互一次,,和虛擬機交互次數太多,有沒有什麼辦法提高效率。
有,那就是如果對class中的多個成員變量都要賦值的話可以調用java的構造函數賦值,這樣不管有多少個函數,只會和虛擬機交互一次,會大大提高效率。
修改後用構造函數賦值的例子如下:
JNIEXPORT void JNICALL Java_getPara_ 1native (JNIEnv *env, jclass thiz, jobject obj)
{
Para_Tag tag;
getPara(&tag);// 取得C++/C中Para_Tag結構體的值
jclass DataCls = env->FindClass("com/Para_Tag"); // 通過class path找到class的標
jmethodID RtnInitId = env->GetMethodID(DataCls, "<init>", "(ISLjava/lang/String;CJ)V");
jstring strTmp = env->NewStringUTF((char *)tag.az);
env->CallVoidMethod(obj, RtnInitId, tag.x, tag.y,strTmp,tag.h,tag.t);
}
這裏用到了C++/C中調用Java中的構造函數,實際上C++/C中還可以調用Java中的其它函數,後面會提到。
2. C++/C指針作爲輸入參數
這種情況下就要具體問題,具體分析了。
如果C++/C代碼需要的僅僅是一個地址,且C++/C不會回傳給值給Java,那麼Java就傳遞一個int型的數即可。
例如:
C++/C: void setValue(int *ps32Addr,int value);
僅僅是給一個地址寫值,那麼
Java:public void setValue(int s32Addr,int value);
JNI:
JNIEXPORT void JNICALL Java_setValue_ 1native (JNIEnv *env, jclass thiz, jint s32Addr,jint value)
{
int *ps32Addr = (int *)s32Addr;
setValue(ps32Addr,value);
}
如果C++/C的指針含有從Java中傳遞的數據信息,就需做如下處理:
typedef struct _Para_tag
{
int x;
short y;
char az[100];
char h;
}Para_Tag;
C++/C : void setPara(Para_Tag *pstTag)
Java:
public class Para_Tag
{
int x;
short y;
String z;
char h;
long t;
}
public void setPara(Para_Tag tag);
JNI:
JNIEXPORT void JNICALL Java_setPara_ 1native (JNIEnv *env, jclass thiz, jobject obj)
{
Para_Tag tag;
jclass DataCls = env->FindClass("com/Para_Tag");
jfieldID dataId = env->GetFieldID(DataCls, "x", "I");
tag.x = env->GetIntField(obj, dataId);
dataId = env->GetFieldID(DataCls, "y", "S");
tag.y = env->GetShortField(obj, dataId);
dataId = env->GetFieldID(DataCls, "z", "Ljava/lang/String;");
jstring jstr = (jstring)env->GetObjectField(obj, dataId);
const char* str;
jboolean isCopye = false;
if(NULL != jstr)
{
str = env->GetStringUTFChars(jstr, &isCopye);
}
if(str == NULL)
{
return;
}
strncpy(tag.az,str,100);
tag.az[99] = '\0';
dataId = env->GetFieldID(DataCls, "h", "C");
tag.h = env->GetCharField(obj, dataId);
dataId = env->GetFieldID(DataCls, "t", "J");
tag.t = (jlong)env->GetLongField(obj, dataId);
setPara(&tag);
}