相信做jni开发的人都知道,jni是什么意思,接口本地调用。
本文记录下本人在开发中关于jni编程中用c++代码编写的简单调用以及复杂数据的调用等等会遇到的一些问题已经解决办法
1、jni简单调用:
首先要在java中定义本地接口,然后在.c或.c++文件中进行编写接口实现的代码。
但是要注意的是c与c++在编写的时候还有差别。
在jni 的JNIEnv,是一个全局的指针,我们在编写的时候通过这个对象来进行jni方法的调用
就拿一个最简单的例子:生成一个java 环境String字符串
c++:
env->NewStringUTF("hahaha");
c:
(*env)->NewStringUTF(env,"hahaha")
2、jni使用回调:
回调的使用是通过我们传过来的对象的类的方法来实现回调的,
JNIEXPORT void JNICALL Java_cp_com_jnidemo_JniFirst_jniCallBack
(JNIEnv *env, jobject srcobj, jdouble a, jdouble b , jobject job){
jclass cla = env->GetObjectClass(job);
jmethodID mid = env->GetMethodID(cla,"callBack","(Ljava/util/String)V");
char buffer[20];
snprintf(buffer,20,"%f",a*b);//将double类型转换为char
jstring native_desc = env->NewStringUTF(buffer);
env->CallVoidMethod(job,mid,native_desc);
}
"(Ljava/util/String)V"
)
这个如何确定呢,现在这个是String类型的,那么如果是int等等呢。
说一下这个参数这样写的意义: 括号里边是回调的时候参数的个数以及类型的签名 ,最后代表当前回调方法有没有返回值V 代表没有返回值.
如果是一个参数的话那么他的写法就是: (回调数据的类型)+返回值类型
String
"(Ljava/util/String)V"
int
"(I)V"
boolean "(Z)V"
那么如果回调的数据类型是一个数组的时候:
String "([Ljava/util/String)V" 也就是在前边加一个大括号
int “([I)V”
boolean "([Z)V"
也就是都加一个大括号就好了
jni签名:
JNI签名:
类型签名 | Java 类型 | 类型签名 | Java 类型 |
Z | boolean | [ | [] |
B | byte | [I | int[] |
C | char | [F | float[] |
S | short | [B | byte[] |
I | int | [C | char[] |
J | long | [S | short[] |
F | float | [D | double[] |
D | double | [J | long[] |
L | fully-qualified-class(全限定的类) | [Z | boolean[] |
l 基本类型
以特定的大写字母表示
3、传递数组:
JNIEXPORT jint JNICALL Java_cp_com_jnidemo_JniFirst_jniArray
(JNIEnv *env, jobject obj, jintArray jarray){
int i,sum = 0;
jsize len = env->GetArrayLength(jarray);
jint* body = env->GetIntArrayElements(jarray,0);
for(int i = 0;i < len;i++){
sum = body[i]+sum;
}
//释放引用对象的空间,防止内存溢出
env->ReleaseIntArrayElements(jarray,body,0);
return sum;
}
要注意释放对象。
4 、复杂对象的传递:
首先要获取到当前传过来的jobject的类型,然后获取相应的方法,进行设置。JNIEXPORT void JNICALL Java_cp_com_jnidemo_JniFirst_setPeopleInfo (JNIEnv *env, jobject srcobj, jobject obj){ jclass cla = env->GetObjectClass(obj); if(cla ==NULL){ cout<<"GetObjectClass failed \n"; } jfieldID ageID = env->GetFieldID(cla,"age","I");//获取年龄id jfieldID nameID = env->GetFieldID(cla,"name","Ljava/lang/String;"); jint age = env->GetIntField(obj,ageID); jstring name= (jstring)env->GetObjectField(obj,nameID); const char * c_name = env->GetStringUTFChars(name,NULL); string str_name = c_name; LOGI("%d", age); LOGI("%s",c_name); env->ReleaseStringUTFChars(name,c_name); cout<<"native age isL "<< age <<"nam is"<< str_name<<endl; }
5、传递HashMap对象:
需要注意的就是签名,还有各种类型的相互转化。JNIEXPORT void JNICALL Java_cp_com_jnidemo_JniFirst_putMap (JNIEnv *env, jobject obj, jobject jmap,jobject callback){ jclass c_map = env->FindClass("java/util/Map"); jmethodID m_getSize = env->GetMethodID(c_map,"size","()I"); jint jsize = env->CallIntMethod(jmap,m_getSize); LOGI("%d",jsize); char log_str[128]; jmethodID m_isempty = env->GetMethodID(c_map,"isEmpty","()Z"); jboolean b= env->CallBooleanMethod(jmap,m_isempty); if(b){ LOGI("map集合为空"); }else{ LOGI("map集合不为空"); } jmethodID m_get = env->GetMethodID(c_map,"clear","()V"); if(m_get == 0){ LOGI("没有找到CLEAR这个方法"); }else{ env->CallVoidMethod(jmap,m_get); LOGI("执行lCLEAR这个方法"); jmethodID size = env->GetMethodID(c_map,"size","()I"); jint s = env->CallIntMethod(jmap,size); int i =s; LOGI("%d",s); jclass call_class = env->GetObjectClass(callback); jmethodID mid = env->GetMethodID(call_class,"callBack","(I)V"); char* result = (char*)malloc(s); //malloc是void类型,可以强转成任意类型 //char* tag= "执行完clear以后的结果size ="; // strcat(tag,result);//将俩个字符指针的字符串拼接 env->CallVoidMethod(callback,mid,s); } }
下边我把我写的Demo地址: http://download.csdn.net/detail/u012808234/9552808