Android JNI 使用的数据结构JNINativeMethod详解

Andoird 中使用了一种不同传统Java JNI的方式来定义其native的函数。其中很重要的区别是Andorid使用了一种Java 和 C 函数的映射表数组,并在其中描述了函数的参数和返回值。这个数组的类型是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[]

上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾中间是用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject. 一个例外是String类,其对应的类为jstring

Ljava/lang/String; String jstring
Ljava/net/Socket; Socket jobject

如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。

例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"


下边是我在做串口通信时的代码:

  1. static const char *classPathName = "android/serialport/SerialPort";
  2. //注意Ljava/io/FileDescriptor;最后的分号,刚开始做时漏了这个分号,查了两天时间,汗
  3. static JNINativeMethod methods[] = {
  4. {"open", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_serialport_SerialPort_open },
  5. {"close", "()V", (void*)android_serialport_SerialPort_close },
  6. };
  7. /*
  8. * Register several native methods for one class.
  9. */
  10. static int registerNativeMethods(JNIEnv* env, const char* className,
  11. JNINativeMethod* gMethods, int numMethods)
  12. {
  13. jclass clazz;
  14. clazz = (*env)->FindClass(env, className);
  15. if (clazz == NULL)
  16. return JNI_FALSE;
  17. if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0)
  18. {
  19. LOGE("register nativers error");
  20. return JNI_FALSE;
  21. }
  22. return JNI_TRUE;
  23. }
  24. /*
  25. * Register native methods for all classes we know about.
  26. *
  27. * returns JNI_TRUE on success.
  28. */
  29. static int registerNatives(JNIEnv* env)
  30. {
  31. if (!registerNativeMethods(env, classPathName,
  32. methods, sizeof(methods) / sizeof(methods[0]))) {
  33. return JNI_FALSE;
  34. }
  35. return JNI_TRUE;
  36. }
  37. /*
  38. * This is called by the VM when the shared library is first loaded.
  39. */
  40. jint JNI_OnLoad(JavaVM* vm, void* reserved)
  41. {
  42. JNIEnv* env = NULL;
  43. jint result = -1;
  44. LOGI("Entering JNI_OnLoad\n");
  45. if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK)
  46. goto bail;
  47. assert(env != NULL);
  48. if (!registerNatives(env))
  49. goto bail;
  50. /* success -- return valid version number */
  51. result = JNI_VERSION_1_4;
  52. bail:
  53. LOGI("Leaving JNI_OnLoad (result=0x%x)\n", result);
  54. return result;
  55. }
static const char *classPathName = "android/serialport/SerialPort";

//注意Ljava/io/FileDescriptor;最后的分号,刚开始做时漏了这个分号,查了两天时间,汗

static JNINativeMethod methods[] = {

  {"open", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_serialport_SerialPort_open },

  {"close", "()V", (void*)android_serialport_SerialPort_close },

};


/*

 * Register several native methods for one class.

 */

static int registerNativeMethods(JNIEnv* env, const char* className,
    JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;

    clazz = (*env)->FindClass(env, className);
    if (clazz == NULL)
        return JNI_FALSE;

    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0)
    {
	LOGE("register nativers error");
        return JNI_FALSE;
    }

    return JNI_TRUE;
}



/*

 * Register native methods for all classes we know about.

 *

 * returns JNI_TRUE on success.

 */

static int registerNatives(JNIEnv* env)

{

  if (!registerNativeMethods(env, classPathName,

                 methods, sizeof(methods) / sizeof(methods[0]))) {

    return JNI_FALSE;

  }

  return JNI_TRUE;

}

/*

 * This is called by the VM when the shared library is first loaded.

 */

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    LOGI("Entering JNI_OnLoad\n");

    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK)
        goto bail;

    assert(env != NULL);

    if (!registerNatives(env))
        goto bail;

    /* success -- return valid version number */
    result = JNI_VERSION_1_4;

bail:
    LOGI("Leaving JNI_OnLoad (result=0x%x)\n", result);
    return result;
}


android_serialport_SeriaPort_open的函数原型为:

static jobject android_serialport_SerialPort_open(JNIEnv *env, jobject thiz, jstring path, jint baudrate);

static void android_serialport_SerialPort_close(JNIEnv *env, jobject thiz);

另外还要注意一点,如果是C++,使用的是env, 如果是C,使用的是(*env),最好参考相应系统中的代码来写。

 

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

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


 

1、JNINativeMethod 结构体的官方定义

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

const char* name;
const char* signature;
void* fnPtr;
} 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. };
/* 
 * 由于gMethods[]是一个<名称,函数指针>对照表,在程序执行时, 
 * 可多次调用registerNativeMethods()函数来更换本地函数的指针, 
 * 从而达到弹性调用本地函数的目的。  
 *具体可以参看http://blog.csdn.net/conowen/article/details/7521340
 */  
static JNINativeMethod gMethods[] = {  
    {"setDataSource",       "(Ljava/lang/String;)V",            (void *)com_media_ffmpeg_FFMpegPlayer_setDataSource},  
    {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)com_media_ffmpeg_FFMpegPlayer_setVideoSurface},  
    {"prepare",             "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_prepare},  
    {"_start",              "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_start},  
    {"_stop",               "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_stop},  
    {"getVideoWidth",       "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getVideoWidth},  
    {"getVideoHeight",      "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getVideoHeight},  
    {"seekTo",              "(I)V",                             (void *)com_media_ffmpeg_FFMpegPlayer_seekTo},  
    {"_pause",              "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_pause},  
    {"isPlaying",           "()Z",                              (void *)com_media_ffmpeg_FFMpegPlayer_isPlaying},  
    {"getCurrentPosition",  "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getCurrentPosition},  
    {"getDuration",         "()I",                              (void *)com_media_ffmpeg_FFMpegPlayer_getDuration},  
    {"_release",            "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_release},  
    {"_reset",              "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_reset},  
    {"setAudioStreamType",  "(I)V",                             (void *)com_media_ffmpeg_FFMpegPlayer_setAudioStreamType},  
    {"native_init",         "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_native_init},  
    {"native_setup",        "(Ljava/lang/Object;)V",            (void *)com_media_ffmpeg_FFMpegPlayer_native_setup},  
    {"native_finalize",     "()V",                              (void *)com_media_ffmpeg_FFMpegPlayer_native_finalize},  
    {"native_suspend_resume", "(Z)I",                           (void *)com_media_ffmpeg_FFMpegPlayer_native_suspend_resume},  
};  
  


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

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

"()" 中的字符表示参数,后面的则代表返回值。例如"()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、付费专栏及课程。

余额充值