前提

Java HashMap 是基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用null值和null键。HashMap是存放引用类型数据的容器,只能存放引用数据类型,不能存放如int、long等基础类型的数据。

这里用实际的例子来演示如何解析HashMap,在这个Sample中,HashMap作为参数从Java传递到Native(C/C++)层,然后在C代码中解析HashMap中的数据。Java中使用HashMap存放学生成绩 “课程 - 分数”键值对,然后把这个学生成绩HashMap数据传递到Native中,用C/C++解析其中的数据,然后以字符串的形式把成绩信息返回到Java。

准备工作:Java中构造和解析HashMap

首先,在Java中构造一个课程分数HashMap。

public HashMapconstructHashMap() {

HashMapcourseScore = new HashMap<>();

courseScore.put("Chinese", 90);

courseScore.put("Math", 95);

courseScore.put("Physics", 100);

courseScore.put("Biology", 85);

return courseScore;

}

在Java中实现返回字符串需求的话,可以使用下面的代码来解析HashMap中的数据。

public String parseHashMap(HashMapcourseScore){

StringBuilder info = new StringBuilder();

Iterator iter = courseScore.entrySet().iterator();

while (iter.hasNext()){

Map.Entry entry = (Map.Entry) iter.next();

String course = (String) entry.getKey();

Integer score = (Integer) entry.getValue();

info.append(course+": ");

info.append(score+". ");

}

return String.valueOf(info);

}

在主函数中依次调用constructHashMap()和parseHashMap()函数,会返回如下所示的字符串:

"Physics: 100. Math: 95. Chinese: 90. Biology: 85. "

Native层虽然使用的是C/C++编程语言,但是使用的确实Java的处理逻辑。在Native中解析HashMap数据结构就要完全按照parseHashMap()函数的逻辑进行数据处理。说白了,就是使用C/C++来模仿Java完成相应操作。

在Native中模仿Java操作其实不难,只是比较 繁琐 !本来Java一句代码可能就需要四五句甚至更多语句来实现相同的功能。为了在Native中能更容易的模仿Java实现,现在我们来把上面的parseHashMap()函数中的语句尽量使用简单语句来实现。

public String parseHashMap2(HashMapcourseScore){

String info = "";

Set set = courseScore.entrySet();

Iterator iter = set.iterator();

while (iter.hasNext()){

Map.Entry entry = (Map.Entry) iter.next();

String course = (String) entry.getKey();

info = info + course + ": ";

Integer score = (Integer) entry.getValue();

info = info + score.intValue() + ". ";

}

return info;

}

进入正题:用C/C++解析Java HashMap

首先Java中声明Native函数。

public class JniManager {

...

public native String nGetHashMapInfo(HashMapinfo);

}

在JNI_OnLoad函数中,函数名映射关系结构体JNINativeMethod中有如下描述。

static const JNINativeMethod gMethods[] = {

{"nGetHashMapInfo", "(Ljava/util/HashMap;)Ljava/lang/String;", (void *) jniGetHashMapInfo}

};

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {

...

jint count = sizeof(gMethods) / sizeof(gMethods[0]);

if (env->RegisterNatives(clazz, gMethods, count) != JNI_OK) {

return result;

}

....

}

在上一篇博客【Android JNI】在C/C++中调用Java中讲到,调用Java方法的步骤为:1. 获取类名jclass,2.获取方法ID jmethodID,3. 使用实例对象jobject和jmethod调用相应的方法。也就是说每次使用一个方法,都要先通过类获取到方法ID,并且要得到对象jobject。

我们在jniGetHashMapInfo()函数中解析Java HashMap结构的课程成绩数据。

jstring jniGetHashMapInfo(JNIEnv *env, jobject object, jobject hashMapInfo) {

// 用于拼接字符串的数组

char buff[100] = {0};

// 用于拼接字符串的“游标”指针

char *pos = buff;

if (hashMapInfo == NULL)

return env->NewStringUTF(buff);

// 获取HashMap类entrySet()方法ID

jclass hashmapClass = env->FindClass("java/util/HashMap");

jmethodID entrySetMID = env->GetMethodID(hashmapClass, "entrySet", "()Ljava/util/Set;");

// 调用entrySet()方法获取Set对象

jobject setObj = env->CallObjectMethod(hashMapInfo, entrySetMID);

// 调用size()方法获取HashMap键值对数量

// jmethodID sizeMID = env->GetMethodID(hashmapClass, "size", "()I");

// jint size = env->CallIntMethod(hashMapInfo, sizeMID);

// 获取Set类中iterator()方法ID

jclass setClass = env->FindClass("java/util/Set");

jmethodID iteratorMID = env->GetMethodID(setClass, "iterator", "()Ljava/util/Iterator;");

// 调用iterator()方法获取Iterator对象

jobject iteratorObj = env->CallObjectMethod(setObj, iteratorMID);

// 获取Iterator类中hasNext()方法ID

// 用于while循环判断HashMap中是否还有数据

jclass iteratorClass = env->FindClass("java/util/Iterator");

jmethodID hasNextMID = env->GetMethodID(iteratorClass, "hasNext", "()Z");

// 获取Iterator类中next()方法ID

// 用于读取HashMap中的每一条数据

jmethodID nextMID = env->GetMethodID(iteratorClass, "next", "()Ljava/lang/Object;");

// 获取Map.Entry类中getKey()和getValue()的方法ID

// 用于读取“课程-分数”键值对,注意:内部类使用$符号表示

jclass entryClass = env->FindClass("java/util/Map$Entry");

jmethodID getKeyMID = env->GetMethodID(entryClass, "getKey", "()Ljava/lang/Object;");

jmethodID getValueMID = env->GetMethodID(entryClass, "getValue", "()Ljava/lang/Object;");

// HashMap只能存放引用数据类型,不能存放int等基本数据类型

// 使用Integer类的intValue()方法获取int数据

jclass integerClass = env->FindClass("java/lang/Integer");

jmethodID valueMID = env->GetMethodID(integerClass, "intValue", "()I");

// 循环检测HashMap中是否还有数据

while (env->CallBooleanMethod(iteratorObj, hasNextMID)) {

// 读取一条数据

jobject entryObj = env->CallObjectMethod(iteratorObj, nextMID);

// 提取数据中key值:String类型课程名字

jstring courseJS = (jstring) env->CallObjectMethod(entryObj, getKeyMID);

if (courseJS == NULL) // HashMap允许null类型

continue;

// jstring转C风格字符串

const char *courseStr = env->GetStringUTFChars(courseJS, NULL);

// 提取数据中value值:Integer类型分数,并转为int类型

jobject scoreObj = env->CallObjectMethod(entryObj, getValueMID);

if (scoreObj == NULL)

continue;

int score = (int) env->CallIntMethod(scoreObj, valueMID);

// 拼接字符串,sprintf函数返回拼接字符个数

int strLen = sprintf(pos, "%s: ", courseStr);

pos += strLen;

int numLen = sprintf(pos, "%d. ", score);

pos += numLen;

// 释放UTF字符串资源

env->ReleaseStringUTFChars(courseJS, courseStr);

// 释放JNI局部引用资源

env->DeleteLocalRef(entryObj);

env->DeleteLocalRef(courseJS);

env->DeleteLocalRef(scoreObj);

}

// 释放JNI局部引用: jclass jobject

env->DeleteLocalRef(hashmapClass);

env->DeleteLocalRef(setObj);

env->DeleteLocalRef(setClass);

env->DeleteLocalRef(iteratorObj);

env->DeleteLocalRef(iteratorClass);

env->DeleteLocalRef(entryClass);

env->DeleteLocalRef(integerClass);

// 生成jstring字符串并返回

return env->NewStringUTF(buff);

}

调用public native String nGetHashMapInfo(HashMapinfo); 函数 和 调用public String parseHashMap2(HashMapcourseScore)函数效果一样,都能返回下面这个成绩信息字符串。

"Physics: 100. Math: 95. Chinese: 90. Biology: 85. "

如果疑问,欢迎留言。

java jni 数据类型_【Android JNI】Native层解析Java复杂数据类型HashMap相关推荐

  1. java 反射机制_基础篇:深入解析JAVA反射机制

    反射的概念 java 的放射机制:在程序运行时,程序有能力获取一个类的所有方法和属性:并且对于任意一个对象,可以调用它的任意方法或者获取其属性 通俗解析:java 文件需要编译成. class 文件才 ...

  2. Android Framework学习(八)之Handler消息机制(Native层)解析

    在深入解析Android中Handler消息机制一文中,我们学习了Handler消息机制的java层代码,这次我们来学习Handler消息机制的native层代码. 在Java层的消息处理机制中,Me ...

  3. android jni 中jnienv,android JNI中JNIEnv類型和jobject類型的解釋

    JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv *env, jobject obj) { cout< } 對於 ...

  4. [Android] Handler源码解析 (Java层)

    之前写过一篇文章,概述了Android应用程序消息处理机制.本文在此文基础上,在源码级别上展开进行概述 简单用例 Handler的使用方法如下所示: Handler myHandler = new H ...

  5. c++和java哪个难_为什么说C语言比Java难?

    C/C++,它和Java确实不太一样.C语言和C++,语言性能虽然高,但语言本身确实很难,我们更愿意称它"造轮子"的语言!也正是因为C语言和C++性能好.粒度细,所以什么都能做,只 ...

  6. java算术表达式_一文了解如何用 Java 进行算术表达式计算

    (给ImportNew加星标,提高Java技能) 编译:ImportNew/唐尤华 如何用Java计算"5+3"."10-40"."10*3" ...

  7. java俄罗斯方块视频_[VIP视频]【A0233】java面对对象编程-俄罗斯方块视频教程 高清视频 百度云 百度网...

    Java视频教程名称:java面对对象编程-俄罗斯方块视频教程  俄罗斯方块视频教程 $ X0 X1 Z: W4 P3 T  e. m3 q百度网盘下载链接: . M% x- I- V5 p( J 游 ...

  8. java 底层运行_从表面到底层丨Java和JVM的运行原理,现在带给你

    Java,编程语言,被创造于90年代初,在经历了这么多年的风风雨雨,Java已经成长为世界第一的编程语言,根据往期以及目前的数据来看,Java的使用频率为全球第一,即使偶尔会有第二第三的情况,但是这依 ...

  9. 数据类型_分享redis中除5种基础数据类型以外的高级数据类型

    众所周知,在redis中的数据类型有String(字符串).hash(哈希).list(列表).set(集合).zset(有序集合)五种.但在这5种之外还有高级数据类型. 今天和大家介绍下常用的高级数 ...

最新文章

  1. DIP第二章习题解答
  2. win7重装系统后设置Python2.7环境
  3. 继承的概念和实现 、 super关键字 、 Object常见方法、抽象类、接口、模板方法模式、成员内部类、匿名内部类
  4. react学习(72)--row上面加样式
  5. 项目总结(采用领域驱动开发方式)
  6. bzoj4504 K个串
  7. 脚本学习python和linux-shell和jQuery(javascript)
  8. Ajax与jQuery、json
  9. linux web接口返回乱码,【bug】测试环境的API接口,返回内容为乱码
  10. Squid 代理服务器应用
  11. java的平方函数_java中的数学函数Math方法记录
  12. 5.stm32L476在freeRTOS下使用低功耗
  13. Android开发 应用软件更新通用方式--强制/非强制/远程控制/浏览器 更新
  14. Java8 新特性之流式数据处理
  15. android调手机通话,手机通话声音小?这样设置能让音量放大几倍,安卓苹果手机通用...
  16. Android 对话框(Dialogs)
  17. 2019年终总结---在前端的成长
  18. 企业为什么需要做APP安全评估?
  19. Vivado Block Design流程(MicroBlaze)
  20. 六:分布式架构存储设计

热门文章

  1. Android四层架构
  2. linux系统监控:记录用户操作轨迹,谁动过服务器
  3. linux下汇编实例
  4. 编程的一些快捷键(转帖)
  5. linux挂载新硬盘,开机自动挂载
  6. [WCF REST] 通过ASP.NET Output Caching实现声明式缓存
  7. 消除warning方法
  8. 热闹庆祝51CTO第二期线下活动圆满完成
  9. 【财务思维课】固定资产是应该买呢还是租或是借呢?
  10. ALV标准过滤功能失效