(注:由于新浪博客不支持 C 注释,所以请将 /* */ 想像替换为 /星 星/)
JNINativeMethod::signature 描述字符串字符意义说明:
1)基本类型对应关系:
标识符 Jni 类型 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
2)基本类型数组:(则以 [ 开始,用两个字符表示)
标识串 Jni 类型 C 类型
[Z jbooleanArray boolean[]
[I jintArray int[]
[J jlongArray long[]
[D jdoubleArray double[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
3)类(class):(则以 L 开头,以 ; 结尾,中间是用 / 隔开的 包 及 类名)
标识串 Java 类型 Jni 类型
L包1/包n/类名; 类名 jobject
例子:
Ljava/net/Socket; Socket jobject
4)例外(String 类):
标识串 Java 类型 Jni 类型
Ljava/lang/String; String jstring
5)嵌套类(类位于另一个类之中,则用$作为类名间的分隔符)
标识串 Java 类型 Jni 类型
L包1/包n/类名$嵌套类名; 类名 jobject
例子:
Landroid/os/FileUtils$FileStatus; FileStatus jobject
首先可以肯定地是 本地动态库函数 不但可以传递 Java 的基本类型,也可以传递更复杂的类型,如:
String、数组 和 自定义的类。
1) Java 基本类型的传递
Java 中的基本类型包括 boolean、byte、char、short、int、long、float、double 这 8 种,
分别对应 jni 类型是 jboolean、jbyte、jchar、jshort、jint、jlong、jfloat、jdouble,
在 jni.h 中找到如下 C 语言类型定义,所以可以直接使用:
/*
* Primitive types that match up with Java equivalents.
*/
#ifdef HAVE_INTTYPES_H
/* C99 */
# include
typedef uint8_t jboolean; /* unsigned 8 bits */
typedef int8_t jbyte; /* signed 8 bits */
typedef uint16_t jchar; /* unsigned 16 bits */
typedef int16_t jshort; /* signed 16 bits */
typedef int32_t jint; /* signed 32 bits */
typedef int64_t jlong; /* signed 64 bits */
typedef float jfloat; /* 32-bit IEEE 754 */
typedef double jdouble; /* 64-bit IEEE 754 */
#else
typedef unsigned char jboolean; /* unsigned 8 bits */
typedef signed char jbyte; /* signed 8 bits */
typedef unsigned short jchar; /* unsigned 16 bits */
typedef short jshort; /* signed 16 bits */
typedef int jint; /* signed 32 bits */
typedef long long jlong; /* signed 64 bits */
typedef float jfloat; /* 32-bit IEEE 754 */
typedef double jdouble; /* 64-bit IEEE 754 */
#endif
/*
* Reference types, in C.
*/
typedef void* jobject;
typedef jobject jclass;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jobjectArray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jobject jthrowable;
typedef jobject jweak;
2) Java String 类型的传递
在 jni.h 中找到如下 C 语言类型定义
/*
* Reference types, in C.
*/
typedef void* jobject;
typedef jobject jstring;
注:Java String 和 C++ string 也是不对等的,在 jni.h 中找到 C++ 语言类型定义如下:
/*
* Reference types, in C++
*/
class _jobject {};
class _jstring : public _jobject {};
typedef _jobject* jobject;
typedef _jstring* jstring;
下面我们看一个以 Java String 做为接收参数和返回值类型的例子:
/*
* Class: wzh_nsc_nativecode
* Method: GetWelcomeWords
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL
Java_wzh_nsc_nativecode_GetWelcomeWords( JNIEnv * env,
jobject this,
jstring jstrUserName )
{
char* pcTransformStr = NULL;
char caCombinStr[200];
jboolean jbIsCopy = JNI_FALSE;
/* 因为 C 语言是如下定义 JNIEnv 类型的 */
/* typedef const struct JNINativeInterface* JNIEnv; */
/* 要用(*env)先将二级指针转成一级指针后方可调用JNINativeInterface结构成员变量 */
/* 如下为 GetStringUTFChars 函数的原型 */
/* const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*); */
/* 如果 用户名字符串指针不为空 的话,则 */
if ( NULL != jstrUserName )
{
/* 开辟新的内存,
然后把 Java 的 String jstrUserName 拷贝到这个内存中,
最后返回指向这个内存地址的指针 */
pcTransformStr = (*env)->GetStringUTFChars( env,
jstrUserName,
/* 第三个参数用来标示是否对 Java 的 String jstrUserName 进行了拷贝的。
如果赋不是 NULL,
而是 jboolean 指针的话则会给该指针所指向的 jboolean 变量内存中传入
JNI_TRUE 或 JNI _FALSE标示是否进行了拷贝。*/
/*
* Manifest constants.
#define JNI_FALSE 0
#define JNI_TRUE 1 */
&jbIsCopy );
}
/* 如果 取得用户名失败 的话,则 */
if( NULL == pcTransformStr )
{
/* 输出的 log 一般是到 /dev/log/ 下的三个设备中,可以用 logcat 工具查看 */
__android_log_write( ANDROID_LOG_INFO, /* 日志信息 */
"nativecode", /* 日志标签 */
/* 日志内容 */
"Import username char string is empty" );
/* 返回用户名不可识别 */
return NULL;
}
memset( caCombinStr, 0, sizeof(caCombinStr) );
sprintf( caCombinStr,
"%s Welcome to you! IsCopy = %d",
pcTransformStr,
jbIsCopy );
/* 输出的 log 一般是到 /dev/log/ 下的三个设备中,可以用 logcat 工具查看 */
__android_log_write( ANDROID_LOG_INFO, /* 日志信息 */
"nativecode", /* 日志标签 */
caCombinStr ); /* 日志内容 */
/* 在你使用完转换生成的字符串缓冲区之后,需要显示调用 ReleaseStringUTFChars 函数,
让 Java 虚拟机释放转换生成的字符串缓冲区空间,
如果不显示的调用的话,Java 虚拟机中会一直保存该缓冲区空间,而不会被垃圾回收器回收,
因此就会导致内存溢出。*/
/* 如下为 ReleaseStringUTFChars 函数的原型 */
/* void (*ReleaseStringUTFChars)(JNIEnv*, jstring, const char*); */
(*env)->ReleaseStringUTFChars( env,
jstrUserName,
pcTransformStr );
/* 如下为 NewStringUTF 函数的原型 */
/* jstring (*NewStringUTF)(JNIEnv*, const char*); */
return (*env)->NewStringUTF( env,
caCombinStr );
}
3) Java 数组类型的传递
Jni 为 Java 基本类型数组提供了如下种类型:
/*
* Reference types, in C.
*/
typedef void* jobject;
typedef jobject jarray;
typedef jarray jobjectArray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
1. 下面我们先来看一个以 Java 一维数组类型 做为接收参数和返回值类型的例子:
/*
* Class: wzh_nsc_nativecode
* Method: DealIntegerArray
* Signature: ([I)[I
* Reference types, in C.
* typedef void* jobject;
* typedef jobject jarray;
* typedef jarray jintArray;
*/
JNIEXPORT jintArray JNICALL
Java_wzh_nsc_nativecode_DealIntegerArray( JNIEnv * env,
jobject this,
jintArray jiaTestScore )
{
jboolean jbIsCopy = JNI_FALSE;
/* 获取数组的长度 */
/* 如下为 GetArrayLength 函数的原型 */
/* jsize (*GetArrayLength)(JNIEnv*, jarray); */
/* "cardinal indices and sizes"
typedef jint jsize; */
jsize jiArraySize = (*env)->GetArrayLength( env,
jiaTestScore );
/* 获取一个指向数组元素的指针 */
/* 如下为 GetIntArrayElements 函数的原型 */
/* jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*); */
jint *piTestResult = (*env)->GetIntArrayElements( env,
jiaTestScore,
/* 第三个参数用来表明返回的数组指针是指向数据的真正存区(值为1时),
还是指向拷贝了数组元素值的另一存区(值为0)*/
/*
* Manifest constants.
#define JNI_FALSE 0
#define JNI_TRUE 1 */
&jbIsCopy );
/* 如果 数组元素个数不大于零(0) 或者 获得首元素指针失败 的话,则 */
if( 0 >= jiArraySize ||
NULL == piTestResult )
{
/* 输出的 log 一般是到 /dev/log/ 下的三个设备中,可以用 logcat 工具查看 */
__android_log_write( ANDROID_LOG_INFO, /* 日志信息 */
"nativecode", /* 日志标签 */
"jintArray parameter is NULL" ); /* 日志内容 */
/* 返回无法处理数组 */
return 0;
}
/* 新建指定元素个数的整型数组函数 NewIntArray 原型如下: */
/* jintArray (*NewIntArray)(JNIEnv*, jsize); */
jintArray jiaModifyScore = (*env)->NewIntArray( env,
jiArraySize );
jint *piModifyScore = (*env)->GetIntArrayElements( env,
jiaModifyScore,
&jbIsCopy );
/* 如果 新建指定元素个数的整型数组失败 或者 获得首元素指针失败 的话,则 */
if( 0 >= jiaModifyScore ||
NULL == piModifyScore )
{
/* 输出的 log 一般是到 /dev/log/ 下的三个设备中,可以用 logcat 工具查看 */
__android_log_write( ANDROID_LOG_INFO, /* 日志信息 */
"nativecode", /* 日志标签 */
"New jintArray is failed" ); /* 日志内容 */
/* 返回无法处理数组 */
return 0;
}
unsigned int uiForCounter = 0;
for( uiForCounter = 0;
uiForCounter < jiArraySize;
uiForCounter++ )
{
piModifyScore[uiForCounter] = piTestResult[uiForCounter] + 10;
}
/* 如下为 ReleaseIntArrayElements 函数的原型 */
/* void (*ReleaseIntArrayElements)(JNIEnv*, jintArray, jint*, jint); */
/* 该函数与 GetIntArrayElements 函数可以说是对应的。
它完成的功能是释放资源和数据更新。
由于 Java 的垃圾收集具有可能改变内存中对象的位置,如不采取必要措施,
被访问的数组指针就可能不再指向正确的存区。因此,对于数组,要么把它“钉”在固定的存区,
要么把它拷贝至固定的存区,总之在访问它的期间要使数组元素总在原地。
作完操作之后,再调用这个函数,解除对它的固定。
另外,在调用这个函数之前,所有更新都没有作用在数组本身上。
第三个参数就是决定更新与否的。
取值 零(0) 时,更新数组并释放所有元素;
取值 JNI_COMMIT 时,更新但不释放所有元素;
取值 JNI_ABORT 时,不作更新但释放所有元素;
#define JNI_COMMIT 1 // copy content, do not free buffer
#define JNI_ABORT 2 // free buffer w/o copying back */
(*env)->ReleaseIntArrayElements( env,
jiaTestScore,
piTestResult,
0 );
(*env)->ReleaseIntArrayElements( env,
jiaModifyScore,
piModifyScore,
0 );
return jiaModifyScore;
}
2. 下面我们先来看一个以 Java 二维数组类型 做为接收参数和返回值类型的例子:
/*
* Class: wzh_nsc_nativecode
* Method: DealTwoDimensionalIntegerArray
* Signature: ([[I)[[I
*/
JNIEXPORT jobjectArray JNICALL
Java_wzh_nsc_nativecode_DealTwoDimensionalIntegerArray( JNIEnv *env,
jobject this,
jobjectArray joaTestInt2DArray )
{
char caCombinStr[200];
jboolean jbIsCopy = JNI_FALSE;
/* 注:二维数组其实就是一维数组的指针数组,每个元素都指向一个一维数组 */
/* 获取数组的长度 */
/* 如下为 GetArrayLength 函数的原型 */
/* jsize (*GetArrayLength)(JNIEnv*, jarray); */
/* "cardinal indices and sizes"
typedef jint jsize; */
int size = (*env)->GetArrayLength( env, joaTestInt2DArray );
/* 得到下标为[0][]的一维数组 */
/* 如下为 GetObjectArrayElement 函数的原型 */
/* jobject (*GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize); */
jarray jaIntArray = (*env)->GetObjectArrayElement( env,
joaTestInt2DArray,
0 );
/* 得到下标为[0][]的一维数组元素个数 */
int length = (*env)->GetArrayLength( env, jaIntArray );
memset( caCombinStr, 0, sizeof(caCombinStr) );
sprintf( caCombinStr,
"size = %d, length = %d",
size,
length );
/* 输出的 log 一般是到 /dev/log/ 下的三个设备中,可以用 logcat 工具查看 */
__android_log_write( ANDROID_LOG_INFO, /* 日志信息 */
"nativecode", /* 日志标签 */
caCombinStr ); /* 日志内容 */
/* 在JNI中,二维数组和String数组都被视为object数组,因为 数组 和String被视为object */
jobjectArray joaReturnInt2DArray;
/* 如下是创建一个jclass的引用,
因为 joaReturnInt2DArray 的元素是 一维 int 数组 的引用,
所以 jcIntArray 必须是 一维 int 数组 的引用,
这一点是如何保证的呢?
注意:FindClass 的参数 "[I",原型如下:
jclass (*FindClass)(JNIEnv*, const char*);
JNI就是通过它来确定引用的类型的,I表示是int类型,[标识是数组。 */
jclass jcIntArray = (*env)->FindClass( env, "[I" );
/* 为 joaReturnInt2DArray 分配空间 */
/* 如下为 NewObjectArray 函数的原型 */
/* jobjectArray (*NewObjectArray)(JNIEnv*, jsize, jclass, jobject); */
joaReturnInt2DArray = (*env)->NewObjectArray( env,
size,
jcIntArray,
NULL );
/* 根据参数传入的二维数组大小创建返回的二维数组 */
int i = 0;
for( i = 0;
i < size;
i++ )
{
/* 得到下标为[0][]的一维数组 */
/* 如下为 GetObjectArrayElement 函数的原型 */
/* jobject (*GetObjectArrayElement)(JNIEnv*, jobjectArray, jsize); */
jaIntArray = (*env)->GetObjectArrayElement( env, joaTestInt2DArray, i );
/* 新建指定元素个数的整型数组函数 NewIntArray 原型如下: */
/* jintArray (*NewIntArray)(JNIEnv*, jsize); */
jintArray jiaTmpIntArray = (*env)->NewIntArray( env, length );
int j = 0;
for( j = 0;
j < length;
j++ )
{
jint *piSrc = (*env)->GetIntArrayElements( env,
jaIntArray, /* 传入的数组 */
&jbIsCopy );
jint *piDes = (*env)->GetIntArrayElements( env,
jiaTmpIntArray, /* 传入的数组 */
&jbIsCopy );
piDes[j] = piSrc[j] + 1;
(*env)->ReleaseIntArrayElements( env,
jaIntArray,
piSrc,
0 );
(*env)->ReleaseIntArrayElements( env,
jiaTmpIntArray,
piDes,
0 );
/* 如果参数传入的是字符串数组的话,可以如下获取每一个字符串
jstring string = ((*env)->GetObjectArrayElement(env, myarray, i));
const char * chars = (*env)->GetStringUTFChars(env, string, 0);
printf("%s n", chars);
(*env)->ReleaseStringUTFChars(env, string, chars); */
}
/* 指定临时一维数组的首地址为数组指针数组(二维数组)的第i个元素的值,原型如下: */
/* void (*SetObjectArrayElement)(JNIEnv*, jobjectArray, jsize, jobject); */
(*env)->SetObjectArrayElement( env, joaReturnInt2DArray, i, jiaTmpIntArray );
/* 删除对数组的当前引用,原型如下: */
/* void (*DeleteLocalRef)(JNIEnv*, jobject); */
(*env)->DeleteLocalRef( env, jiaTmpIntArray );
}
return joaReturnInt2DArray;
}
4)在 native 方法中创建 Java 用户自定义类对象
如果要创建 Java 用户自定义类对象,首先要能访问类的构造函数,
/* 首先要创建一个 Java 用户自定义类的引用,通过 FindClass 函数来完成,
参数同前面介绍的创建 java/lang/String 对象的引用类似,只不过类名称变成自定义类的名称 */
jclass jcCCustomClass = (*env)->FindClass( env, "Lwzhnsc/CCustomClass;" );
/* 然后通过 GetMethodID 函数获得这个类的构造函数,原型如下:
jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
注意这里方法的名称是”<init>”,它表示这是一个构造函数 */
jmethodID jmiInit = (*env)->GetMethodID( env,
jcCCustomClass,
"<init>",
"(D)V" );
// 注:JNI规定调用构造函数的时候传递的方法名应该为<init>
jvalue jvArgs[1];
jvArgs[0].d = 1.369;
/* 生成了一个 Java 用户自定义类对象 */
jobject joCustomClassObj = (*env)->NewObjectA( env,
jcCCustomClass,
jmiInit,
jvArgs );
5)将异常抛给 Java 代码处理
JNI提供了实现这种功能的机制,可以通过如下面代码抛出一个 Java 代码可以接收的异常。
jclass jcError;
/* void (*ExceptionDescribe)(JNIEnv*); */
(*env)->ExceptionDescribe( env );
/* void (*ExceptionClear)(JNIEnv*); */
(*env)->ExceptionClear( env );
jcError = (*env)->FindClass( env, “Ljava/lang/IllegalArgumentException” );
/* jint (*ThrowNew)(JNIEnv *, jclass, const char *); */
(*env)->ThrowNew( env, jcError, “throw from C code” );
如果要抛出其他类型的异常,替换掉FindClass的参数即可。
这样,在 Java 中就可以接收到本机方法中抛出的异常。
一、当 Android 的 Virtual Machine 执行到 System.loadLibrary( "动态库名" ) 函数时,
首先会去执行 C 语言动态库里的 JNI_OnLoad 函数。
它的用途有两个:
1)告诉 Virtual Machine 当前动态库使用了哪个版本的 Jni。
如果当前动态库中没有提供 JNI_OnLoad 函数,
Virtual Machine 会默认为动态库使用的是最老的 Jni 1.1 版本。
由于新版 Jni 做了许多扩充,例如 Jni 1.4 的 java.nio.ByteBuffer。
2)动态库的开发者可以在 JNI_OnLoad 函数中进行动态库内的初始化设置(Initialization),
将此动态库中提供的各个本地函数(Native Function)登记到 Virtual Machine 里,
以便能加快以后调用动态库中的本地函数的效率,就是初始化设置的重要一项。
应用层级的 Java 类通过 Virtual Machine 才能调用到动态库中的本地函数。
如果没有注册登记过的话,Virtual Machine 就在 动态库名.so 里寻找要调用的本地函数。
如果需要连续调用很多次且每次都需要寻找一遍的话,会多花许多时间。
因此 C 语言动态库开发者可以自已将动态库中的本地函数向 Virtual Machine 进行注册登记。
代码示例:(注:由于新浪博客不支持 C 注释,所以请将 /* */ 想像替换为 /星 星/)
jint
JNI_OnLoad( JavaVM* vm,
void* reserved )
{
jint jintResult = -1;
JNIEnv* env = NULL;
/*Reference types, in C.
typedef void* jobject;
typedef jobject jclass; */
jclass cls = NULL;
/* typedef struct {
const char* name; /* Java 代码中调用的函数名字 */
const char* signature; /* 描述了函数的 参数 和 返回值 */
void* fnPtr; /* 函数指针转成无符号指针 */
} JNINativeMethod;
其中比较复杂的是第二个参数,
例如 "()V" 或 "(II)V" 或 "(Ljava/lang/String;)V"
实际上这些字符是与函数的 参数 及 返回值 类型是一一对应的,
括号()中的字符表示参数,括号后面的则代表返回值,
例如 "()V" 就表示 void 函数名();
"(II)V" 就表示 void 函数名( int, int );
"(Ljava/lang/String;)V" 就表示 void 函数名( jstring );
具体的每一个字符所表示的意义下面部分有所详见 */
/* 动态库中的本地函数信息数组 */
JNINativeMethod aJNINativeMethod[] = {
{ "MeasureDistance",
"(Ljava/lang/String;)V",
(void*)Java_MyJni_MyNDK_MyDemo_MyJniNDKDemo_MeasureDistance }
};
/* #if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif */
/* JavaVM::GetEnv 原型为 jint (*GetEnv)(JavaVM*, void**, jint); */
/* GetEnv()函数返回的 Jni 环境对每个线程来说是不同的,*/
/* 因此我们必须在每次进入函数时都要重新获取 */
if ( JNI_OK != (*vm)->GetEnv( vm,
(void**)env,
JNI_VERSION_1_6 ) )
{
/* 输出的 log 一般是到 /dev/log/ 下的三个设备中,可以用 logcat 工具查看 */
__android_log_write( ANDROID_LOG_INFO, /* 日志信息 */
"MyJniDemo", /* 日志标签 */
"Call JavaVM::GetEnv failed" ); /* 日志内容 */
return jintResult; /* 此时返回的是负壹(-1) */
}
/* 如果 将动态库中的本地函数向 Virtual Machine 进行注册登记失败 的话,则 */
/* 由于 aJNINativeMethod 是一组 函数名称 与 函数指针 的对照表,
在程序执行期间可以多次调用registerNativeMethods函数来更换注册登记本地函数 */
/* #if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif */
/* Reference types, in C.
typedef void* jobject;
typedef jobject jclass; */
/* struct JNINativeInterface 里的函数指针
jint (*RegisterNatives)( JNIEnv*,
jclass,
const JNINativeMethod*,
jint ); */
cls = (*env)->FindClass( env,
/* 下面的字符串就是描述 Java 代码中的主类 */
"MyJni/MyNDK/MyDemo/MyJniNDKDemo" );
if ( 0 > (*env)->RegisterNatives( env,
cls,
aJNINativeMethod,
sizeof( aJNINativeMethod ) /
sizeof( aJNINativeMethod[0] ) ) )
{
/* 输出的 log 一般是到 /dev/log/ 下的三个设备中,可以用 logcat 工具查看 */
__android_log_write( ANDROID_LOG_INFO, /*日志信息*/
"MyJniDemo", /*日志标签*/
"Register native methods failed" ); /*日志内容*/
return jintResult; /* 此时返回的是负壹(-1) */
}
jintResult = JNI_VERSION_1_6;
/* 此函数回传 JNI_VERSION_1_6 宏值给 Virtual Machine,
于是 Virtual Machine 就知道当前动态库所使用的 Jni 版本了 */
return jintResult; /* JNI_VERSION_1_6(0x00010006) */
}
二、JNI_OnUnload 函数与 JNI_OnLoad 函数相对应。
在 JNI_OnLoad 函数中进行的动态库内的初期化设置,
要在 Virtual Machine 释放该动态库时调用 JNI_OnUnload 函数来进行善后清除。
同 Virtual Machine 调用 JNI_OnLoad 一样,
调用 JNI_Unload 函数时,也会将 JavaVM 的指针做为第一个参数传递,原型如下:
jint
JNI_OnUnload( JavaVM* vm,
void* reserved );
三、JNINativeMethod::signature 描述字符串字符意义说明:
1)基本类型对应关系:
标识符 Jni 类型 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
2)基本类型数组:(则以 [ 开始,用两个字符表示)
标识串 Jni 类型 C 类型
[Z jbooleanArray boolean[]
[I jintArray int[]
[J jlongArray long[]
[D jdoubleArray double[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
3)类(class):(则以 L 开头,以 ; 结尾,中间是用 / 隔开的 包 及 类名)
标识串 Java 类型 Jni 类型
L包1/包n/类名; 类名 jobject
例子:
Ljava/net/Socket; Socket jobject
4)例外(String 类):
标识串 Java 类型 Jni 类型
Ljava/lang/String; String jstring
5)嵌套类(类位于另一个类之中,则用$作为类名间的分隔符)
标识串 Java 类型 Jni 类型
L包1/包n/类名$嵌套类名; 类名 jobject
例子:
Landroid/os/FileUtils$FileStatus; FileStatus jobject
from: http://blog.sina.com.cn/s/blog_4c451e0e0101339i.html