jni提供了一种java和c/c++交互的方式
字符串的处理我感觉比较有用的方法有三个
newStringUTF这个用来新建一个java字符串
getStringUTFLength获取java字符串的长度
getStringUTFChars这个用来获取java字符串的指针
releaseStringUTFChars释放获取到的指针
java方法声明:
public static native String getString();
public static native void setString(String data);
java方法名和c++方法名的对应,请直接使用Javah生成,不要想自己去写,规则太麻烦了。
c++代码实现
JNIEXPORT jstring JNICALL Java_JNITest_getString(JNIEnv *env, jclass thisObj){
const char * data ="hello";
//新建java字符串
jstring jdata= env->NewStringUTF(data);
return jdata;
}
JNIEXPORT void JNICALL Java_JNITest_setString(JNIEnv *env, jclass thisObj, jstring data){
//转换成c++字符串
const char* showData=env->GetStringUTFChars(data,NULL);
string msg(showData);
//释放引用
env->ReleaseStringUTFChars(data,showData);
cout<<msg<<endl;
}
jni对一维数组的处理
jni对java一维数组的方法比较实用的有几个
xxx具体用其他数据类型替换
byte对应char
java的char对应c++wchar
NewxxxArray新建java的一维数组
SetxxxArrayRegion用c++数组值赋值给java数组
GetArrayLength获取数组长度
GetIntArrayRegion用java数组值赋值给c++数组
public static native int[] getIntData();
public static native void show(int[] data);
我的建议是先把数据转成c++格式,处理完后直接复制给java数组,不要直接对java数组进行数据操作
JNIEXPORT jintArray JNICALL Java_JNITest_getIntData(JNIEnv *env, jclass thisObj){
int tmpData[7]={1,3,5,6,7,3,4};
//新建java数组
jintArray data = env->NewIntArray(7);
//把c++数组的值传给java数组
env->SetIntArrayRegion(data,0,7,(jint *)tmpData);
return data;
}
JNIEXPORT void JNICALL Java_JNITest_show___3I(JNIEnv *env, jclass thisObj, jintArray data){
//获取数组长度
int len=env->GetArrayLength(data);
int* cData=new int[len];
//把java数组的值赋值给c++数组
env->GetIntArrayRegion(data,0,len,(jint *)cData);
for(int i=0;i<len;i++){
cout<<cData[i];
}
delete[] cData;
}
对于获取数组指针,然后操作java数组的方法没有给出,因为效果不如先转换格式,然后按照一种规范操作好用
jni对二维数组的处理对二维数组的处理是建立在一维数组之上的
我的观念还是优先考虑java数组和c++数组的转换,不直接从java数组中取数据进行操作
FindClass这个方法主要是获取jclass的,所谓的二维数组就是jobjectarray里放着jxxxarray的一维数组,这样理解就好多了,关键是获取到相应的一维数组进行操作
Get\SetxxxxArrayElement直接获取某个数组里的元素
public static native int[][] getData();
public static native void showArray(int[][] data);
JNIEXPORT jobjectArray JNICALL Java_JNITest_getData(JNIEnv *env, jclass thisObj){
int data[2][2]={{1,2},{3,4}};
//获取数组的class
jclass intClass = env->FindClass("[I");
//新建object数组,里面是int[]
jobjectArray jdata=env->NewObjectArray(2,intClass,NULL);
//赋值
for(int i=0;i<2;i++){
jintArray intdata = env->NewIntArray(2);
env->SetIntArrayRegion(intdata,0,2,(jint*)&data[i]);
env->SetObjectArrayElement(jdata,i,intdata);
}
return jdata;
}
JNIEXPORT void JNICALL Java_JNITest_showArray(JNIEnv *env, jclass thisObj, jobjectArray array){
//获取数组长度
int len = env->GetArrayLength(array);
int data[10][10];
for(int i=0;i<len;i++){
jintArray intdata =(jintArray)env->GetObjectArrayElement(array,i);
//赋值
env->GetIntArrayRegion(intdata,0,len,(jint*)&data[i]);
}
for(int i=0;i<len;i++){
for(int j=0;j<len;j++){
cout<<data[i][j]<<",";
}
}
}
这里再介绍几个方法,在特定场合要比先转换来得快
GetxxxArrayElements直接获取数组的指针
releasexxxArrayElemets释放指针
这是一对操作
jni对对象的处理jni对对象的处理其实比较八股,流程基本都是一样的,这次不列举方法了,常用的都在例子里
首先获取jclass,通过FindClass或者getobjectclass
然后获取方法id或者成员变量id
最后执行方法或者拿到成员变量的值
获取Id的时候有一个问题就是获取签名,我的感觉不要自己去记,直接用javap去查,然后拷贝过来就行
class Student{
public static int ID=123;
private String name;
public Student(String name){
this.name=name;
}
public void show(){
System.out.println(name);
}
}
JNIEXPORT void JNICALL Java_JNITest_showStudent(JNIEnv *env, jclass thisObj, jobject stuObj){
//获取jclass
jclass stuClass =env->FindClass("Student");
//获取方法id
jmethodID showID = env->GetMethodID(stuClass,"show","()V");
//执行方法
env->CallVoidMethod(stuObj,showID);
//获取类成员变量id
jfieldID fieldID =env->GetStaticFieldID(stuClass,"ID","I");
//获取变量的值
jint stuID =env->GetStaticIntField(stuClass,fieldID);
cout<<"ID is "<<stuID<<endl;
//新建对象
jmethodID initID = env->GetMethodID(stuClass,"<init>","(Ljava/lang/String;)V");
jstring name = env->NewStringUTF("second");
jobject stuNewObj =env->NewObject(stuClass,initID,name);
env->CallVoidMethod(stuNewObj,showID);
}
jni补充和异常处理
前面基本总结了常见的一些情况,包括对数组,字符串,对象的处理,基本上满足了效果。
具体的例子都在https://git.oschina.net/xpbob/jni.git
我用的环境是mingw,环境变了的话,就修改makefile吧
我还有没总结的部分:异常处理
异常处理其实和java对象处理差不多,你可以选择先new一个异常,然后调用throw方法
也可以通过throwNew这个方法,简单易用,只需要传递jclass和信息就行,推荐大家使用
我感觉写代码还是各自有各自的流程处理,java和c++的交互只在获取值和返回值的时候最好。
这里再说点细节:
类引用只在本地方法返回时有效,不要全局保存。但是java还提供了newGlobalRef来锁定,用完后调用deleteGlobalRef去除引用,但是这在设计上感觉并不是很好的方法,我建议还是需要的时候再获取一个吧。
关于文件:
java都是大端,英特尔架构cpu基本是小端,arm架构cpu基本是大端,用c++读取java文件时,只要不是char(这里的char就是java里的byte),必须考虑字节的翻转。
Cygwin编译
gcc -mno-cygwin -D __int="long long" -I xxx -shared -Wl,--add-stdcall-alias
c的调用
(*env)->NewStringUTF(env,xxxx);
共同学习,写下你的评论
评论加载中...
作者其他优质文章