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对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);
}前面基本总结了常见的一些情况,包括对数组,字符串,对象的处理,基本上满足了效果。
具体的例子都在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);共同学习,写下你的评论
评论加载中...
作者其他优质文章
 
                 
             
			 
					