有时候我们的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);
}
本文出自 “肉肉之家” 博客,请务必保留此出处http://4895268.blog.51cto.com/4885268/1371620