您的位置:首页 > 移动开发 > Android开发

android jni代码编写规则--整理总结

2012-04-05 13:40 591 查看
JNI层的代码其实比较简单,难点是要掌握c++和java数据类型的转换,明白java程序是运行在虚拟机中的,特别是函数并不是可以互相调用,jni中的内存概念并没有暴露给java虚拟机进程等。

一. 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中一切对象、接口以及数组都是对象)

/article/1389478.html

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开发提高篇

/article/1389478.html

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的基本数据类型是无符号的

本文转自:/article/2890031.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: