前言

最近在做中间件相关,遇到一个JAVA和C之间枚举值通过JNI传递的问题。我们知道C语言的枚举型是一个集合,其每个枚举成员是一个整型常量;而JAVA的枚举其本质是一个类,继承自Enum类,每个枚举成员是枚举类型常量。他们之间是不同的两种数据类型,不能直接进行转换。Java枚举和C枚举的转换本质是Java枚举类型和int整型的转换。

两种枚举的特点

新创建的枚举类型继承自Enum类。

Enum类有两个成员变量,name和ordinal。name为枚举常量的名字,ordinal为枚举常量声明时的顺序(从0开始)。

Enum类有一个ordinal()方法,用来获取变量ordinal的值;还有一个values()方法用来获取所有的枚举常量,返回值是一个枚举的数组。

JAVA的枚举类型构造函数一定是私有的,因为要保证枚举是单例的。因此我们不能在JNI里使用AllocObject、NewObject这种需要用到构造函数的方法构建一个枚举型。

JAVA枚举类型的成员都是static的,JNI中可以直接访问

C的枚举型中第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1。

一个例子

知道了两种枚举的特点,我们就可以开始进行转换了。下面我们通过一个例子说明如何进行转换:

我的老板有个三岁的女儿,她很喜欢吃水果,她知道苹果是红色的,香蕉是黄色的。可是别的水果呢?于是她希望有个万能的小精灵告诉她水果的颜色。于是老板找到了我……

啥也不说了,开始吧。先定义两个C枚举,用来描述水果和颜色,它们是底层沟通信息用的。

typedef enum {FRUIT_APPLE,FRUIT_GRAPE,FRUIT_BANANA,FRUIT_MAX
}FRUIT;typedef enum {COLOR_RED,COLOR_PURPLE,COLOR_YELLOW,COLOR_MAX
}COLOR;

然后定义两个JAVA枚举,也用来表示水果和颜色,应用间聊天的时候会用到它们进行描述。


public enum EnFruit {APPLE,GRAPE,BANANA,FRUIT_MAX;
}public enum EnColor {RED,PURPLE,YELLOW,COLOR_MAX;
}

很好,我们离成功又进了一步。

现在我们老板的女儿问出了第一个问题:葡萄什么颜色的?

水果颜色识别器:什么?什么水果??什么是葡萄??

应用:就是这个!!这个就是!!!

两位大佬语言不通,鸡同鸭讲,吵成一团。

于是JNI大哥来帮忙。

extern "C" jobject JNICALL Java_com_example_jnitest_MainActivity_getFruitColor(JNIEnv *env, jobject object, jobject objFruit){jclass clazzFruit = env->FindClass("com/example/jnitest/EnFruit"); jmethodID j_method_fruitOrdinal = env->GetMethodID(clazzFruit,"ordinal","()I");int fruit = env->CallIntMethod(objFruit,j_method_fruitOrdinal);COLOR color = get_fruit_color((FRUIT)fruit);//自定义水果颜色识别器,识别水果颜色}
  • 他首先通过FindClass查找Java中代表水果的类,这个方法的参数是类的全名。使用UTF-8编码,其中分隔符使用/表示。
  •  然后通过GetMethodID方法获取水果类的一个实例方法ID。该方法的第一个参数是一个Java类;第二个参数是UTF-8编码的方法名;第三参数是UTF-8编码的方法签名。
  • 这里需要注意,GetMethodID获取的方法是非静态的,需要类的实例才能调用它。接下来的CallIntMethod方法就是通过一个类的实例调用了ordinal方法。Call<type>Method这一系列方法的返回值是type;第一个参数就是类的实例(这里类的实例是由Java直接传给JNI的objFruit);第二个参数就是上一步中获取的实例方法ID;后面是可变参数列表。这里的ordinal方法没有参数,故不填写。

现在终于把应用说的水果翻译成水果颜色识别器能看懂的话,识别器很高兴。

水果颜色识别器:这个水果是紫色啦!

应用:紫色?眼前的黑不是黑,你说的紫是什么紫?

语言不通真是烦恼,于是JNI大哥又忙活起来。当然最后不要忘记DeleteLocalRef释放资源,把翻译用的草稿本还回去留给别人继续用。

extern "C" jobject JNICALL Java_com_example_jnitest_MainActivity_getFruitColor(JNIEnv *env, jobject object, jobject objFruit){jclass clazzFruit = env->FindClass("com/example/jnitest/EnFruit");jmethodID j_method_fruitOrdinal = env->GetMethodID(clazzFruit,"ordinal","()I");int fruit = env->CallIntMethod(objFruit,j_method_fruitOrdinal);COLOR color = get_fruit_color((FRUIT)fruit);//自定义水果颜色识别器,识别水果颜色jclass clazzColor = env->FindClass("com/example/jnitest/EnColor");jobject objColor;jfieldID j_fieldID_RED = env->GetStaticFieldID(clazzColor,"RED","Lcom/example/jnitest/EnColor;");jfieldID j_fieldID_PURPLE = env->GetStaticFieldID(clazzColor,"PURPLE","Lcom/example/jnitest/EnColor;");jfieldID j_fieldID_YELLOW = env->GetStaticFieldID(clazzColor,"YELLOW","Lcom/example/jnitest/EnColor;");switch (color){case COLOR_RED:objColor = env->GetStaticObjectField(clazzColor,j_fieldID_RED);break;case COLOR_PURPLE:objColor = env->GetStaticObjectField(clazzColor,j_fieldID_PURPLE);break;case COLOR_YELLOW:objColor = env->GetStaticObjectField(clazzColor,j_fieldID_YELLOW);break;default:objColor = nullptr;}env->DeleteLocalRef(clazzFruit);env->DeleteLocalRef(clazzColor);return objColor;
}
  • 还是通过 FindClass查找Java中代表颜色的类。
  • 由于Java的enum成员都是静态的,我们通过GetStaticFieldID获取颜色类的静态成员ID。它的第一个参数是一个Java类;第二个参数是使用UTF-8编码的属性名;第三个参数是使用UTF-8编码的属性类型签名。
  • 最后我们就可以通过类的GetStaticObjectField方法获取一个静态的成员,这个方法可以获得一个类的实例。他的第一个参数是一个Java类;第二个参数是这个静态成员的ID。

上面的方法虽然可行,但是在旁边的JNI二哥看不下去了,还没等大哥写完演算,就一把抢过来,在EnColor的枚举里自定义了一个static方法,获取指定顺序的枚举。改成了下面的样子。

public enum EnColor {RED,PURPLE,YELLOW,COLOR_MAX;public static EnColor getColor(int ordinal){EnColor colors[] = values();if(ordinal < colors.length && ordinal >= 0)return colors[ordinal];return null;}
}

对应的JNI如下修改,顿时清爽了很多。

extern "C" jobject JNICALL Java_com_example_jnitest_MainActivity_getFruitColor(JNIEnv *env, jobject object, jobject objFruit){jclass clazzFruit = env->FindClass("com/example/jnitest/EnFruit");jmethodID j_method_fruitOrdinal = env->GetMethodID(clazzFruit,"ordinal","()I");int fruit = env->CallIntMethod(objFruit,j_method_fruitOrdinal);COLOR color = get_fruit_color((FRUIT)fruit);//自定义水果颜色识别器,识别水果颜色jclass clazzColor = env->FindClass("com/example/jnitest/EnColor");jmethodID j_method_getColor = env->GetStaticMethodID(clazzColor,"getColor","(I)Lcom/example/jnitest/EnColor;");jobject objColor = env->CallStaticObjectMethod(clazzColor,j_method_getColor,(int)color);env->DeleteLocalRef(clazzFruit);env->DeleteLocalRef(clazzColor);return objColor;
}
  • 修改之后我们通过GetStaticMethodID获得一个静态方法ID。该方法与GetMethodID方法的参数都是一样的。
  • 然后我们通过CallStaticObjectMethod调用了一个我们自定义的静态方法,返回值是一个类的实例。CallStatic<type>Method这一类方法第一个参数是一个Java类,它不像Call<type>Method的方法那样需要类的实例;其余的参数和Call<type>Method方法是一样的。

这里有个小问题,我们在JNI里拿到的objColor并没有释放,不会造成内存泄漏的问题吗?首先,我们当然不能释放他,因为JAVA里还需要用到它。其次,Java是个好孩子,他最后会把从JNI里拿到的对象释放掉的,我们完全不用担心。

好了,现在应用可算知道识别器说的是哪个紫色了,赶紧把这个结果告诉我们老板的女儿。大功告成!

老板女儿:可是昨天我们吃的葡萄不是紫色的呀?爸爸你骗我,你骗我。(大哭)

老板(怒):你明天不用来上班了!

摔!!!


参考文献:

枚举的本质

java enum(枚举)使用详解 + 总结

C语言中的enum(枚举)用法

JNI完全指南(二)——类与异常

JNI完全指南(四)——对象操作

Java枚举和C枚举的转换——JNI笔记相关推荐

  1. java枚举类型转换_java枚举类型enum值与整数的转换

    java编程中偶尔会用到枚举,为了直观,我们通常将枚举值设置为形象的单词,方便理解和使用.枚举类型相当于数据库 中字典表,但是枚举只有字典表的值,缺少其他用来表示和值一一对应的字段,当我们在数据库中保 ...

  2. Java枚举(用Java普通类模拟枚举的实现原理及JDK枚举API使用示例)

    枚举的特点: ◆类型安全(Type Safety)  ◆紧凑有效的枚举数值定义(Compact, Efficient Declaration of Enumerated Values)  ◆无缝的和程 ...

  3. java集合,多线程,面向对象,枚举类,异常等所有常用的方法总和

    extends //继承 Instanceof //多态里面的比较是否new 的对象为他 abstract //抽象类 final // 不可修改,最终的 如果在类前面加那么表示此类不可以被继承 st ...

  4. java枚举数字_Java枚举类型的使用,数值的二进制表示

    一.Java枚举类型的使用 首先请看这段代码: packagejava上课;public classEnumTest {public static voidmain(String[] args) { ...

  5. java枚举 数字_java 枚举

    Java中枚举的线程安全性及序列化问题 --枚举是如何保证线程安全的 要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,enum就和class一样,只是一个关键字 ...

  6. Java OOP 8 实用类(枚举、包装类、日期类、Random类)

    Java OOP 8 实用类 文章目录 Java OOP 8 实用类 一.学习目标 二.Java API 三.枚举 3.1为什么使用枚举? 3.2什么是枚举? 3.3如何使用枚举? 3.4常见错误 四 ...

  7. Java语言中的枚举类型

    枚举类型(Enumerated Type)在编程语言中常用,程序员必备食粮,下面随着我的思路来认识一下枚举类型. 是什么 枚举类型在java中是一种基本数据类型.它用于声明一组命名的常数,当一个变量有 ...

  8. Java基础教程(15)--枚举类型

      枚举类型定义了一个枚举值的列表,每个值是一个标识符.例如,下面的语句声明了一个枚举类型,用来表示星期的可能情况: public enum Day {SUNDAY, MONDAY, TUESDAY, ...

  9. 浅谈在Java开发中的枚举的作用和用法

    2019独角兽企业重金招聘Python工程师标准>>> 在枚举出现之前,如果想要表示一组特定的离散值,往往使用一些常量.例如: [java] view plain copy pack ...

最新文章

  1. OpenVINO + OpenCV实现点头与摇头识别验证
  2. c语言练习题及答案)(1),c语言练习题(带详解答案)1.pdf
  3. atm c语言流程图_c语言ATM机程序
  4. 2009计算机网络考研大题,2009年计算机考研统考真题网络部分分析
  5. origin数据平滑_研发工程师必备:20条实用origin技能,让作图效率飞起来
  6. 上班之第一个无聊周末
  7. 提升开发效率的 Chrome 开发者工具快捷键参考
  8. 使用 imitator 实现前后端分离开发中的数据模拟与静态资源映射
  9. 形象解释 undefined 和 null 之间的区别 ​
  10. VFP DownFileX下载异常问题
  11. 网页监控系统设计之使用mjpg-streamer
  12. EasyDarwin源码分析(三)——rtsp-client
  13. 发改委交通运输部印发《推进“互联网+”便捷交通 促进智能交通发展的实施方案》(全文)...
  14. Win10编译和测试libModbus
  15. 直线回归和相关------(四)直线相关系数和决定系数(原理与公式推导)
  16. form表单提交方式
  17. 50 行代码爬取链家租房信息
  18. Android与ARM处理器
  19. 产品经理 demo html,18个UI demo设计实例,深挖让用户愉悦的小惊喜
  20. php制作日历带节日实验目的,php制作日历

热门文章

  1. go中反射机制中Value.Elem()应该如何理解?
  2. 小程序Cannot read property 'elem' of undefined
  3. SM4国密算法整理-流程图
  4. web作业—简历信息管理系统
  5. Tiva单片机——简易示波器(UART串口屏)
  6. matlab示波器如何反白,stc12单片机写的简易示波器代码,求大神答疑
  7. 神州泰岳王宁:从高校团委书记到中关村富豪
  8. 【招生简章】北京交通大学计算机与信息技术学院2023年【工程博士】研究生申请考核制招生实施办法
  9. hbuilder写的html运行不出来
  10. 布隆过滤器是什么?一起来瞅瞅!