一. java参数类型和jni本地参数类型对照
基本类型
Java 类型 jni本地类型 描述
boolean jboolean C/C++ unsigned 8 bits
byte jbyte C/C++ signed 8 bits
char jchar C/C++ unsigned 16 bits
short jshort C/C++ signed 16 bits
int jint C/C++ signed 32 bits
long jlong C/C++ signed 64 bits
float jfloat C/C++ 32位浮点型
double jdouble C/C++ 64位浮点型
void void N/A
表一
对象类型
Object jobject 任何Java对象,或者没有对应
java类型的对象
Class jclass class对象
String jstring 字符串对象
表二
数组类型
boolean[] jbooleanArray 布尔型数组 unsigned
byte[] jbyteArray 比特型数组 signed
char[] jcharArray 字符型数组 unsigned
short[] jshortArray 短整型数组 signed
int[] jintArray 整型数组 signed
long[] jlongArray 长整型数组 signed
float[] jfloatArray 浮点型数组
double[] jdoubleArray 双浮点型数组
Object[] jobjectArray 任何对象的数组
表三
JNI引用类型与Java的对应关系如下树层次图:
1. java中的返回值void和JNI中的void是完全对应的。
2. java中的基本数据类型(boolean ,byte , char ,short ,int,long,float,double八种)在JNI中对应的数据类型只要在前面加上j就对应了(jboolean ,jbyte , jchar ,jshort ,jint,jlong,jfloat,jdouble)。
JNI中还有个Java中没有的jsize,定义如下:
typedef jint jsize;
其实jsize整型是用来描述基本指标和大小。
3. java中的对象,包括类库中定义的类、接口以及自定义的类接口,都对应于JNI中的jobject。
4. java中基本数据类型的数组对应与JNI中的j<type>array类型。(type就是上面说的8种基本数据类型)
5. java中对象的数组对应于JNI中的jobjectArray类型。(在java中一切对象、接口以及数组都是对象)
http://blog.csdn.net/xyz_lmn/article/details/6956003
http://www.cnblogs.com/liangwind/archive/2009/08/18/1925515.html
Java基本类型的精度
java 的基本数据类型是不存在有符号和无符号这种概念的. JAVA中的基本数据类型不存在无符号的,它们的取值范围是固定的,不会随着机器硬件环境或者操作系统的改变而改变。
简单类型 字节数 范围/精度
float 4 32位IEEE754单精度
double 8 64位IEEE754双精度
byte 1 -128到127
short 2 -32,768到32,767
int 4 -2,147,483,648到2,147,483,647
long 8 -9,223,372,036,854,775,808到9,223,372,036,854,775,807
char 2 整个Unicode字符集
boolean 1 True或者false
像byte 是范围是 -128到127, 你想要变为 0到255 怎么办, 跟 0XFF 做与运算 就可以了.
如 byte bb , 如果你想赋值它值 255, 那是不行的, 就算赋值了, bb 的值也是 255 对 256 求模后的值 -1
如果你只是想取他 0到255 的值, 还是很简单的,
bb & 0XFF , 如 bb = -1, 那 bb & 0XFF 结果为 255,
这个与运算后的结果会隐式转换为int 类型的, 因为 byte 放不下了.
与运算 还是很快的, 比加减法还快的.
http://www.stuhack.com/biancheng/java/35169.html
二.jni层使用java的基本类型数据
对于上面八种基本的数据类型boolean ,byte , char ,short ,int,long,float,double,jni层的c++代码可以用强制直接转换成对于长度的c/c++类型数据。
如:unsigned char tmp = (unsigned char) m_jboolean;
unsigned short tmp = (unsigned short)m_jchar;
或者同长度类型的数据就可以直接赋值的,int tmp = m_jint;
三.jni层对数组的使用
JNI通过JNIEnv提供的操作Java数组的功能。它提供了两个函数:一个是操作java的简单型数组的,另一个是操作对象类型数组的。
1. 操作java的简单型数组
因为速度的原因,简单类型的数组作为指向本地类型的指针暴露给本地代码。因此,它们能作为常规的数组存取。这个指针是指向实际的Java数组或者Java数组的拷贝的指针。另外,数组的布置保证匹配本地类型。
为了存取Java简单类型的数组,你就要要使用GetXXXArrayElements函数(见表三),XXX代表了数组的类型。这个函数把Java数组看成参数,返回一个指向对应的本地类型的数组的指针。
完整的函数族见下表:
函数 Java 数组类型 本地类型
GetBooleanArrayElements jbooleanArray jboolean
GetByteArrayElements jbyteArray jbyte
GetCharArrayElements jcharArray jchar
GetShortArrayElements jshortArray jshort
GetIntArrayElements jintArray jint
GetLongArrayElements jlongArray jlong
GetFloatArrayElements jfloatArray jfloat
GetDoubleArrayElements jdoubleArray jdouble
当你对数组的存取完成后,要确保调用相应的ReleaseXXXArrayElements函数,参数是对应Java数组和GetXXXArrayElements返回的指针。如果必要的话,这个释放函数会复制你做的任何变化(这样它们就反射到java数组),然后释放所有相 关的资源。
例如:
static jint com_ginwave_fs_com_HWRC_GetRecogRange(JNIEnv* env, jclass clazz, jintArray Handle)
{
unsigned long *pHandle = NULL;
int ret = 0;
jint *tmpHandle = env->GetIntArrayElements(Handle, 0);
pHandle = (unsigned long *)tmpHandle;
ret = (int)HWRC_GetRecogRange(pHandle);
env->ReleaseIntArrayElements(Handle, tmpHandle, 0);
return r
}
获取数组的长度:
jint theArrayLength = env->GetArrayLength(Frame);
创建一个新的函数数组簇如下:
NewBooleanArray
NewByteArray
NewCharArray
NewShortArray
NewIntArray
NewLongArray
NewFloatArray
NewDoubleArray
参数为数组长度,如:
jbyte *list;
jbyteArray byteArray = NULL;
byteArray = env->NewByteArray(len);
if (byteArray)
env->SetByteArrayRegion(byteArray, 0, len, list);
关于函数簇GetXXXArrayRegion和SetXXXArrayRegion,其中XXX为基本类型。
例如:
env->SetByteArrayRegion(buffer, destOffset, count, (const jbyte *)address + srcOffset);
Setxxx的方向是从JNI层往java层传递;
env->GetByteArrayRegion(buffer, srcOffset, count, (jbyte *)address + destOffset);
而Getxxx的方向则是数据从java层向jni层传递。
这里是获取简单型数组中的数据供jni或者下层使用,如果需要在jni层设置java
中对于的简单型数组的话,就需要使用到接下来讲到的对象类型的一些操作。
总结下,有以下几簇函数:
GetArrayLength
NewXXXArray
GetXXXArrayElements
ReleaseXXXArrayElements
GetXXXArrayRegion
SetXXXArrayRegion
对于数据,暂时遇到这些函数了。。。
2. 操作java对象类型数据
Java对象做为引用被传递到本地方法中,所有这些Java对象的引用都有一个共同的父类型jobject(相当于java中的Object类是所有类的父类一样)。
1). string对象
从java程序中传过去的String对象在本地方法中对应的是jstring类型,jstring类型和c中的char*不同,所以如果你直接当做char*使用的话,就会出错。因此在使用之前需要将jstring转换成为c/c++中的char*,这里使用JNIEnv的方法转换。
static jstring com_prompt_getLine(JNIEnv *env, jobject obj, jstring prompt)
{
char buf[128];
const char *str = (*env)->GetStringUTFChars(env, prompt, 0);
printf("%s", str);
env->ReleaseStringUTFChars(prompt, str);
...
}
这里使用GetStringUTFChars方法将传进来的prompt(jstring类型)转换成为UTF-8的格式,就能够在本地方法中使用了。
注意:在使用完你所转换之后的对象之后,需要显示调用ReleaseStringUTFChars方法,让JVM释放转换成UTF-8的string的对象的空间,如果不显示的调用的话,JVM中会一直保存该对象,不会被垃圾回收器回收,因此就会导致内存溢出。
下面是访问String的一些方法:
GetStringUTFChars 将jstring转换成为UTF-8格式的char*
GetStringChars 将jstring转换成为Unicode格式的char*
ReleaseStringUTFChars 释放指向UTF-8格式的char*的指针
ReleaseStringChars 释放指向Unicode格式的char*的指针
NewStringUTF 创建一个UTF-8格式的String对象
NewString 创建一个Unicode格式的String对象
GetStringUTFLength 获取UTF-8格式的char*的长度
GetStringLength 获取Unicode格式的char*的长度
提供给两个jstring和char *互相转换的函数:
/* c/c++ string turn to java jstring */
static jstring strTojstring(JNIEnv* env, const unsigned char* pStr)
{
int strLen = strlen((const char*)pStr);
jclass jstrObj = env->FindClass("java/lang/String");
jmethodID methodId = env->GetMethodID(jstrObj, "", "([BLjava/lang/String;)V");
jbyteArray byteArray = env->NewByteArray(strLen);
jstring encode = env->NewStringUTF("utf-8");
env->SetByteArrayRegion(byteArray, 0, strLen, (jbyte*)pStr);
return (jstring)env->NewObject(jstrObj, methodId, byteArray, encode);
}
//check ok!
/* java jstring turn to c/c++ string */
static char* jstringTostr(JNIEnv* env, jstring jstr)
{
char* pStr = NULL;
jclass jstrObj = env->FindClass("java/lang/String");
jstring encode = env->NewStringUTF("utf-8");
jmethodID methodId = env->GetMethodID(jstrObj, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray byteArray = (jbyteArray)env->CallObjectMethod(jstr, methodId, encode);
jsize strLen = env->GetArrayLength(byteArray);
jbyte *jBuf = env->GetByteArrayElements(byteArray, JNI_FALSE);
if (jBuf > 0)
{
pStr = (char*)malloc(strLen + 1);
if (!pStr)
{
return NULL;
}
memcpy(pStr, jBuf, strLen);
pStr[strLen] = 0;
}
env->ReleaseByteArrayElements(byteArray, jBuf, 0);
return pStr;
}
// check ok!
2) 访问java对象
JNI提供的另外一个功能是在本地代码中使用Java对象。通过使用合适的JNI函数,你可以创建Java对象,get、set 静态(static)和 实例(instance)的域,调用静态(static)和实例(instance)函数。JNI通过ID识别域和方法,一个域或方法的ID是任何处理域和方法的函数的必须参数。
下表列出了用以得到静态(static)和实例(instance)的域与方法的JNI函数。每个函数接受(作为参数)域或方法的类,它们的名称,符号和它们对应返回的jfieldID或jmethodID。
函数 描述
GetFieldID 得到一个实例的域的ID
GetStaticFieldID 得到一个静态的域的ID
GetMethodID 得到一个实例的方法的ID
GetStaticMethodID 得到一个静态方法的ID
下面以一个例子来说明用法:上下层之间需要传递一个或者多个结构体值。
c/c++结构体定义:
typedef struct tagTHWFrame{
short left;
short top;
short width;
short height;
} THWFrame;
当然在java层也需要定义一个匹配的类出来:
public class THWFrame{
public short left;
public short top;
public short width;
public short height;
}
注意貌似这里只能定义成public的。
下面是jni层相关的代码,主要思想是对java对应类对象的属性域获得ID值后一个一个访问。
/* int HWRC_SetInputBox( unsigned long *pHandle, const THWFrame *pFrame ); */
static void ObjectTOTHWFrameStruct(JNIEnv* env, jobjectArray Frame, THWFrame *pFrame, int index)
{
jobject obj = env->GetObjectArrayElement(Frame, index);
jclass cls = env->GetObjectClass(obj);
jfieldID left = env->GetFieldID(cls, "left", "S");
pFrame[index].left = (short)env->GetShortField(obj, left);
jfieldID top = env->GetFieldID(cls, "top", "S");
pFrame[index].top = (short)env->GetShortField(obj, top);
jfieldID width = env->GetFieldID(cls, "width", "S");
pFrame[index].width = (short)env->GetShortField(obj, width);
jfieldID height = env->GetFieldID(cls, "height", "S");
pFrame[index].height = (short)env->GetShortField(obj, height);
}
static jint com_ginwave_fs_com_HWRC_SetInputBox(JNIEnv* env, jclass clazz,
jintArray Handle, jobjectArray Frame)
{
unsigned long *pHandle = NULL;
THWFrame *pFrame = NULL;
int frame_len = 0;
int ret = 0;
jint *tmpHandle = env->GetIntArrayElements(Handle, 0);
pHandle = (unsigned long *)tmpHandle;
jint theArrayLength = env->GetArrayLength(Frame);
frame_len = theArrayLength;
pFrame = (THWFrame *)malloc( sizeof(THWFrame) * frame_len );
for( int i = 0; i < frame_len; i++ ){
ObjectTOTHWFrameStruct(env, Frame, pFrame, i);
}
ret = HWRC_SetInputBox(pHandle, (const THWFrame *)pFrame);
env->ReleaseIntArrayElements(Handle, tmpHandle, 0);
free(pFrame);
frame_len = NULL;
return ret;
}
// {"HWRC_SetInputBox", "([I[Ljava/com/ginwave/fs/com/THWFrame)I" , (void *)com_ginwave_fs_com_HWRC_SetInputBox },
// check ok!
/* int HWRC_GetInputBox( unsigned long *pHandle, THWFrame *pFrame ); */
static void THWFrameStructTOObject(JNIEnv* env, jobjectArray Frame, THWFrame *pFrame, int index)
{
jobject obj = env->GetObjectArrayElement(Frame, index);
jclass cls = env->GetObjectClass(obj);
jfieldID left = env->GetFieldID(cls, "left", "S");
env->SetShortField(obj, left, (short)pFrame[index].left);
jfieldID top = env->GetFieldID(cls, "top", "S");
env->SetShortField(obj, top, (short)pFrame[index].top);
jfieldID width = env->GetFieldID(cls, "width", "S");
env->SetShortField(obj, width, (short)pFrame[index].width);
jfieldID height = env->GetFieldID(cls, "height", "S");
env->SetShortField(obj, height, (short)pFrame[index].height);
}
static jint com_ginwave_fs_com_HWRC_GetInputBox(JNIEnv* env, jclass clazz,
jintArray Handle, jobjectArray Frame)
{
unsigned long *pHandle = NULL;
THWFrame *pFrame = NULL;
int frame_len = 0;
int ret = 0;
jint *tmpHandle = env->GetIntArrayElements(Handle, 0);
pHandle = (unsigned long *)tmpHandle;
jint theArrayLength = env->GetArrayLength(Frame);
frame_len = theArrayLength;
pFrame = (THWFrame *)malloc( sizeof(THWFrame) * frame_len );
ret = HWRC_GetInputBox(pHandle, pFrame);
for( int i = 0; i < frame_len; i++ ){
THWFrameStructTOObject(env, Frame, pFrame, i);
}
env->ReleaseIntArrayElements(Handle, tmpHandle, 0);
free(pFrame);
frame_len = NULL;
return ret;
}
// {"HWRC_GetInputBox", "([I[Ljava/com/ginwave/fs/com/THWFrame)I" , (void *)com_ginwave_fs_com_HWRC_GetInputBox },
// check ok!
其中,比较难理解的应该是函数的签名了,下面是他们的一些规则:
这个数组的类型是JNINativeMethod,定义如下:
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
第一个变量name是Java中函数的名字。
第二个变量signature,用字符串是描述了函数的参数和返回值
第三个变量fnPtr是函数指针,指向C函数。
其中比较难以理解的是第二个参数,例如
"()V"
"(II)V"
"(Ljava/lang/String;Ljava/lang/String;)V"
实际上这些字符是与函数的参数类型一一对应的。
"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();
"(II)V" 表示 void Func(int, int);
具体的每一个字符的对应关系如下
字符 Java类型 C类型
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
数组则以"["开始,用两个字符表示
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray boolean[]
objects对象 Lfully-qualified-class-name; L类名
Arrays数组 [array-type [数组类型
方法参数或者返回值为java中的对象时,必须以“L”加上其路径,不过此路径必须以“/”分开,自定义的对象也使用本规则,不在包中时直接“L”加上类名称。比如说 java.lang.String为“java/lang/String”,com.nedu.jni.helloword.Student为"com /nedu/jni/helloword/Student"
方法参数或者返回值为数组时类型前加上[,例如[I表示 int[],[[[D表示 double[][][],即几维数组就加几个[。
JNI函数中始终包含两个必要的参数:JNIEnv* env, jclass clazz
JNIEnv *――它是一个接口指针,用于定位函数表中的函数!
在JNI规范中一般称 为 “Interface Pointer”。看到这儿好像和过程调用很类似了!是的,JNI中的操作过程,就是面向过程的!后面的jobject是 一个指向该类的指针,类似与C语言中的this。这个第二个参数是变化的,当该方法为类的实例方法时该参数为jobject;当该方法为类方法 (即静态方法)时该参数为jclass,指向该类的class。
通过ndk编程来得到jni层头文件的时候,这第二个参数对于staic方法,生成出来的就是jclass,而对于非staic方法,生成出来的就是jobject。
从上图可知,jobject包含了其实概括了所有的java类型,也就是说,像上图中的非jobject类型的数据,在传递参数的时候都可以以jobject类型传递下去。比如说,如果要java中要传递一个二(多)维int数组下去,就可以包装成jobjectArray传下去,只不过对应的签名要弄成[[I了。
对于访问java对象的方法
在本地方法中调用Java对象的方法的步骤:
①.获取你需要访问的Java对象的类:
jclass cls = (*env)->GetObjectClass(env, obj); // FindClass(“android/util/log”)
使用GetObjectClass方法获取obj对应的jclass。 // 直接搜索类名,需要是static修饰的类。
②.获取MethodID:
jmethodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V");
// GetStaticMethodID(…) , 获取静态方法的ID
使用GetMethdoID方法获取你要使用的方法的MethdoID。其参数的意义:
env-->JNIEnv
cls-->第一步获取的jclass
"callback"-->要调用的方法名
"(I)V"-->方法的Signature, 签名同前面的JNI规则。
③.调用方法:
(*env)->CallVoidMethod(env, obj, mid, depth);
// CallStaticIntMethod(….) , 调用静态方法
使用CallVoidMethod方法调用方法。参数的意义:
env-->JNIEnv
obj-->通过本地方法穿过来的jobject
mid-->要调用的MethodID(即第二步获得的MethodID)
depth-->方法需要的参数(对应方法的需求,添加相应的参数)
注:这里使用的是CallVoidMethod方法调用,因为没有返回值,如果有返回值的话使用对应的方法,在后面会提到。
CallVoidMethod CallStaticVoidMethod
CallIntMethod CallStaticVoidMethod
CallBooleanMethod CallStaticVoidMethod
CallByteMethod CallStaticVoidMethod
…
其实jni中还有很多很多的接口函数这里没有列举,可以直接参考源码:
$ find frameworks/base type d -name jni
./voip/jni
./rfid/jni
./freestylus/jni
./native/graphics/jni
./drm/jni
./tests/BrowserTestPlugin/jni
./services/jni
./packages/TtsService/jni
./media/jni
./media/libdrm/mobile1/include/jni
./media/libdrm/mobile1/src/jni
./graphics/jni
./core/jni
./opengl/tests/gl_jni/jni
./opengl/tests/gl2_jni/jni
./opengl/tests/gldual/jni
这么多jni目录都可以参考,其中主要是core/jni目录了。
四、关于异常
异常接口有:
jniThrowException(env, "java/lang/RuntimeException", "Method called after release()");
env->ThrowNew(env->FindClass("java/io/IOException"),"CWJLog Error, IOException");
doThrow(env, "java/lang/IllegalStateException", msg);
使用Throw,自己构造(没用过)
jclass clazz = env->FindClass("java/io/IOException");
jmethodID methodId = env->GetMethodID(clazz, "<init>", "()V");
jthrowable throwable = env->NewObject(clazz, methodId);
env->Throwthrowable);
参考网址:
http://blog.csdn.net/xyz_lmn/article/details/6959545
Android JNI入门第三篇——jni头文件分析
http://blog.csdn.net/xyz_lmn/article/details/6966259
Android JNI入门第四篇——Android.mk文件分析
http://blog.csdn.net/xyz_lmn/article/details/7017420
Android JNI开发提高篇
http://blog.csdn.net/xyz_lmn/article/details/6956003
Android JNI入门第二篇——Java参数类型与本地参数类型对照
http://wenku.baidu.com/view/e9e28ca1b0717fd5360cdc18.html
JNI入门
http://www.ibm.com/developerworks/cn/java/j-jni/
使用 Java Native Interface 的最佳实践
http://helloxuweifu.iteye.com/blog/1168647
http://blog.csdn.net/kangyaping/article/details/6584027
JNI函数调用大全
http://newfaction.net/2010/11/30/java-jni-getfieldid-and-getmethodid-and-parameter-description.html
java jni GetFieldID 和 GetMethodID 以及参数的说明
http://hi.baidu.com/spmno/blog/item/7d4d764ea78a6809b3de0588.html
jni中使用数组的几个方法
http://xxw8393.blog.163.com/blog/static/3725683420107109411366/
JNI 返回结构体参数
http://www.cnblogs.com/nicholas_f/archive/2010/11/30/1892124.html
JNI中java类型与C/C++类型对应关系
http://blog.csdn.net/sunny09290/article/details/6884994
JNI数据类型
http://www.cnblogs.com/liangwind/archive/2009/08/18/1925515.html
jni --c/c++ 数据类型、数组、对象
http://www.cnblogs.com/diyunpeng/archive/2009/09/24/1573296.html
Java有符号数与无符号数
http://www.stuhack.com/biancheng/java/35169.html
Java的基本数据类型是无符号的
本文转自:http://blog.csdn.net/lizhiguo0532/article/details/7219357