Android的JNI数据类型的详解

123 篇文章 1 订阅

转自:http://blog.csdn.net/conowen/article/details/7523145

http://blog.csdn.net/conowen/article/details/7524744

/********************************************************************************************
 * author:conowen@大钟                                                                                                                          
 * E-mail:conowen@hotmail.com                                                                                                             
 * http://blog.csdn.net/conowen                                                                                                              
 * 注:本文为原创,仅作为学习交流使用,转载请标明作者及出处。      

 ********************************************************************************************/


       在Java中有两类数据类型:primitive types,如,int, float, char;另一种为reference types,如,类,实例,数组。


 注意:数组,不管是对象数组还是基本类型数组,都作为reference types存在,有专门的JNI方法取数组中每个元素。



1、void

java的void与JNI的void是一致的。


2、基本数据类型





3、对象类型



相比基本类型,对象类型的传递要复杂得多。不能对Jstring进行直接操作。

  1. //如下使用方式是错误的,因为jstring不同于C语言中的char *类型。  
  2. Java_com_conowen_test_testActivity_test(JNIEnv *env, jobject obj, jstring str)  
  3. {  
  4. /* ERROR: incorrect use of jstring as a char* pointer */  
  5. printf("%s", str);  
  6. ...  
  7. }  



注意:

  1. typedef jint jsize;  




3.1、GetStringUTFChars与ReleaseStringUTFChars函数简单说明(跳到3.2有更方便的函数)

        JNI支持Unicode/UTF-8字符编码互转。Unicode以16-bits值编码;UTF-8是一种以字节为单位变长格式的字符编码,并与7-bitsASCII码兼容。UTF-8字串与C字串一样,以NULL('\0')做结束符, 当UTF-8包含非ASCII码字符时,以'\0'做结束符的规则不变。7-bit ASCII字符的取值范围在1-127之间,这些字符的值域与UTF-8中相同。当最高位被设置时,表示多字节编码。


  1. //调用GetStringUTFChars,把一个Unicode字串转成UTF-8格式字串  
  2.   
  3. Java_com_conowen_test_testActivity_test(JNIEnv *env, jobject obj, jstring str)  
  4. {  
  5. char buf[128];  
  6. const jbyte *cbyte;  
  7. cbyte= (*env)->GetStringUTFChars(env, str, NULL);  
  8. if (cbyte== NULL) {  
  9. return NULL;  
  10. }  
  11. printf("%s", cbyte);  
  12. (*env)->ReleaseStringUTFChars(env, str, cbyte);  
  13.   
  14. scanf("%127s", buf);  
  15. return (*env)->NewStringUTF(env, buf);  
  16.   
  17. //或者return (*env)->NewStringUTF(env, "hello world");  
  18.  }  

         上述函数中,有isCopy参数,当该值为JNI_TRUE,将返回str的一个拷贝;为JNI_FALSE将直接指向str的内容。 注意:当isCopy为JNI_FALSE,不要修改返回值,不然将改变java.lang.String的不可变语义。一般会把isCopy设为NULL,不关心Java VM对返回的指针是否直接指向java.lang.String的内容。

         注意:在调用GetStringChars之后,一定要调用ReleaseStringChars做释放,(Unicode -> UTF-8转换的原因)。不管在调用GetStringChars时为isCopy赋值JNI_TRUE还是JNI_FALSE,因不同JavaVM实现的原因,ReleaseStringChars可能释放内存,也可能释放一个内存占用标记。

 

 3.2、GetStringRegion/GetStringUTFRegion函数简单说明

 

 因为这两个函数不涉及内存操作,所以较GetStringUTFChars使用要简单。也不用进行释放指针之类的操作,非常方便。(推荐使用)


  1. Java_com_conowen_test_testActivity_test(JNIEnv *env, jobject obj, jstring str)  
  2. {  
  3.   
  4. char outputbuf[128], inputbuf[128];  
  5. int len = (*env)->GetStringLength(env, str);  
  6. (*env)->GetStringUTFRegion(env, str, 0, len, outbuf);  
  7. printf("%s", outputbuf);  
  8. scanf("%s", inputbuf);  
  9. return (*env)->NewStringUTF(env, inbuf);  
  10. }  

GetStringUTFRegion有两个主要的参数,start 和 length, 这两个参数以Unicode编码计算. 该函数会做边界检查,所以可能抛出StringIndexOutOfBoundsException。 


3.3、GetStringLength/GetStringUTFLength函数简单说明


前者是Unicode编码长度,后者返回的是是UTF编码长度。


4、数组类型





JNI对每种数据类型的数组都有对应的函数。


4.1、常见错误操作:

  1. /* 直接操作数组是错误的 */  
  2. Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)  
  3. {  
  4. int i, sum = 0;  
  5. for (i = 0; i < 10; i++) {  
  6. sum += arr[i];  
  7. }  
  8. }  

4.2、使用

void Get<Type>ArrayRegion(JNIEnv *env,<ArrayType> array, jsize start,jsize len, <NativeType> *buf);

进行操作

参数说明:

env: the JNIEnv interface pointer.

array: a reference to an array whose elements are to be copied.将要被拷贝的目标数组<ArrayType>

start: the starting index of the array elements to be copied.(数组的起始位置)

len: the number of elements to be copied.(拷贝元素的个数)buf:the destination buffer.存放结果的本地数组<NativeType>

返回值:void



  1. Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)  
  2. {  
  3. jint buf[10];  
  4. jint i, sum = 0;  
  5. (*env)->GetIntArrayRegion(env, arr, 010, buf);  
  6. for (i = 0; i < 10; i++) {  
  7. sum += buf[i];  
  8. }  
  9. return sum;  
  10. }  
  11.   
  12. JNI中数组的基类为jarray,其他如jintArray都是继承自jarray。  


4.3、使用<NativeType> *Get<Type>ArrayElements(JNIEnv *env,<ArrayType> array, jboolean *isCopy);进行数组操作



参数说明:

env: the JNIEnv interface pointer.array: a reference to the primitive array whose elements are tobe accessed.(目标数组)

isCopy: a pointer to a jboolean indicating whether a function

返回值:返回指向Java数组的一个直接的指针


  1. 使用实例:  
  2.   
  3. Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintArray arr)  
  4. {  
  5. jint *carr;  
  6. jint i, sum = 0;  
  7. carr = (*env)->GetIntArrayElements(env, arr, NULL);  
  8. if (carr == NULL) {  
  9. return 0/* exception occurred */  
  10. }  
  11. for (i=0; i<10; i++) {  
  12. sum += carr[i];  
  13. }  
  14. (*env)->ReleaseIntArrayElements(env, arr, carr, 0);  
  15. return sum;  
  16. }  
  17.   
  18.   
  19. )  

更多数组操作函数:

5、另外一些有用的宏定义(来自jni.h)



  1. #define JNI_FALSE   0  
  2. #define JNI_TRUE    1  
  3.   
  4. #define JNI_VERSION_1_1 0x00010001  
  5. #define JNI_VERSION_1_2 0x00010002  
  6. #define JNI_VERSION_1_4 0x00010004  
  7. #define JNI_VERSION_1_6 0x00010006  
  8.   
  9. #define JNI_OK          (0)         /* no error */  
  10. #define JNI_ERR         (-1)        /* generic error */  
  11. #define JNI_EDETACHED   (-2)        /* thread detached from the VM */  
  12. #define JNI_EVERSION    (-3)        /* JNI version error */  
  13.   
  14. #define JNI_COMMIT      1           /* copy content, do not free buffer */  
  15. #define JNI_ABORT       2           /* free buffer w/o copying back */ 

1、JNINativeMethod 结构体的官方定义

  1. typedef struct {  
  2.   
  3. const char* name;  
  4. const char* signature;  
  5. void* fnPtr;  
  6. } JNINativeMethod;  

第一个变量name是Java中函数的名字。

第二个变量signature,用字符串是描述了Java中函数的参数和返回值

第三个变量fnPtr是函数指针,指向native函数。前面都要接 (void *)

第一个变量与第三个变量是对应的,一个是java层方法名,对应着第三个参数的native方法名字

更多内容请查看之前博文:http://blog.csdn.net/conowen/article/details/7521340


示例:
  1. /*  
  2.  * 由于gMethods[]是一个<名称,函数指针>对照表,在程序执行时,  
  3.  * 可多次调用registerNativeMethods()函数来更换本地函数的指针,  
  4.  * 从而达到弹性调用本地函数的目的。   
  5.  *具体可以参看http://blog.csdn.net/conowen/article/details/7521340 
  6.  */    
  7. static JNINativeMethod gMethods[] = {    
  8.     {"setDataSource",       "(Ljava/lang/String;)V",            (void *)com_media_ffmpeg_FFMpegPlayer_setDataSource},    
  9.     {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)com_media_ffmpeg_FFMpegPlayer_setVideoSurface},    
  10.     {"prepare",             "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_prepare},    
  11.     {"_start",              "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_start},    
  12.     {"_stop",               "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_stop},    
  13.     {"getVideoWidth",       "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getVideoWidth},    
  14.     {"getVideoHeight",      "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getVideoHeight},    
  15.     {"seekTo",              "(I)V",                             (void *)com_media_ffmpeg_FFMpegPlayer_seekTo},    
  16.     {"_pause",              "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_pause},    
  17.     {"isPlaying",           "()Z",                              (void *)com_media_ffmpeg_FFMpegPlayer_isPlaying},    
  18.     {"getCurrentPosition",  "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getCurrentPosition},    
  19.     {"getDuration",         "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getDuration},    
  20.     {"_release",            "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_release},    
  21.     {"_reset",              "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_reset},    
  22.     {"setAudioStreamType",  "(I)V",                             (void *)com_media_ffmpeg_FFMpegPlayer_setAudioStreamType},    
  23.     {"native_init",         "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_native_init},    
  24.     {"native_setup",        "(Ljava/lang/Object;)V",            (void *)com_media_ffmpeg_FFMpegPlayer_native_setup},    
  25.     {"native_finalize",     "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_native_finalize},    
  26.     {"native_suspend_resume""(Z)I",                           (void *)com_media_ffmpeg_FFMpegPlayer_native_suspend_resume},    
  27. };    
  28.     


主要是第二个参数比较复杂:

括号里面表示参数的类型,括号后面表示返回值。

"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Fun();

"(II)V" 表示 void Fun(int a, int b);

这些字符与函数的参数类型的映射表如下:


2、第二个参数之基本数据类型




3、第二个参数之对象类型与数组类型


对象类型:以"L"开头,以";"结尾,中间是用"/" 隔开。如上表第1个

数组类型:以"["开始。如上表第2个(n维数组的话,则是前面多少个"["而已,如"[[[D"表示“double[][][]”)

对象数组类型:上述两者结合,如上表第3个


3.1、对象类型与数组类型的举例:



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值