博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
IntelliJ IDEA平台下JNI编程(五)—本地C代码创建Java对象及引用
阅读量:4483 次
发布时间:2019-06-08

本文共 5069 字,大约阅读时间需要 16 分钟。

本文学习如何在C代码中创建Java对象和对象数组,前面我们学习了C代码中访问Java对象的属性和方法,其实在创建对象时本质上也就是调用构造函数,因此本文知识学习起来也很轻松。有了前面学习数组创建的方法后,C代码创建对象数组同样很容易,下面开始学习吧~

1. C代码创建Java对象

创建Java对象本质就是调用构造函数,这与上一篇文章中提到的调用方法使用方法一致。下面直接贴代码:

package com.huachao.java;/** * Created by HuaChao on 2017/03/23. */public class HelloJNI { static { System.loadLibrary("HelloJNI"); } private String name; private HelloJNI(String name) { this.name = name; } public static native HelloJNI getInstance(); public static void main(String[] args) { HelloJNI obj = HelloJNI.getInstance(); System.out.println(obj.name); } }

接下来在c代码中完成对getInstance()的实现。

#include
#include
#include "com_huachao_java_HelloJNI.h" JNIEXPORT jobject JNICALL Java_com_huachao_java_HelloJNI_getInstance (JNIEnv * env,jobject thisObj){ jclass cls = (*env)->FindClass(env, "com/huachao/java/HelloJNI"); //获取构造函数的ID jmethodID midInit = (*env)->GetMethodID(env, cls, "
", "(Ljava/lang/String;)V"); if (NULL == midInit) return NULL; // 调用构造函数 jstring name=(*env)->NewStringUTF(env, "HuaChao"); jobject newObj = (*env)->NewObject(env, cls, midInit, name); return newObj; }

有一点需要注意,在调用HellJNI(String name)构造函数时,需要先将c的本地字符串转为jstring类型,即调用NewStringUTF函数,再作为参数传入。运行结果如下:

HuaChao

2. C代码创建对象数组

创建对象数组其实就是结合对象的创建和数组的创建来实现,都是前面学过的知识。在HelloJNI.java中加入getArray函数,并且修改main函数如下:

public static native HelloJNI[] getArray(String[] names);  public static void main(String[] args) { String[] names = { "HuaChao", "Lianjin"}; HelloJNI[] arr = HelloJNI.getArray(names); for (HelloJNI obj : arr) { System.out.println("name:" + obj.name); } }

在本地C代码中,对应getArray函数的实现如下:

JNIEXPORT jobjectArray JNICALL Java_com_huachao_java_HelloJNI_getArray (JNIEnv * env,jobject thisObj,jobjectArray names){      // 获取HelloJNI类的引用       jclass clazz = (*env)->FindClass(env, "com/huachao/java/HelloJNI");       //获取构造函数的ID       jmethodID midInit = (*env)->GetMethodID(env, clazz, "
", "(Ljava/lang/String;)V"); // 获取数组长度 jsize length = (*env)->GetArrayLength(env, names); jobjectArray outJNIArray = (*env)->NewObjectArray(env, length, clazz, NULL); //遍历names数组 int i; for (i = 0; i < length; i++) { jstring name = (*env)->GetObjectArrayElement(env, names, i); if (NULL == name) return NULL; jobject newObj = (*env)->NewObject(env, clazz, midInit, name); (*env)->SetObjectArrayElement(env, outJNIArray, i, newObj); } return outJNIArray; }

最后,运行如下:

name:HuaChaoname:Lianjin

3. C代码对Java类的局部和全局引用

与Java代码类似,在C代码函数里面创建的对象时,对对象的引用为局部引用,当函数执行结束时,引用无效。但是如果在函数外对对象进行引用,引用会一直有效,直到程序结束。前面我们频繁地用到了jclass和jmethodID以及jfieldID,下面我们尝试将其作为c代码的全局引用。

首先看看Java中代码。

package com.huachao.java;/** * Created by HuaChao on 2017/03/23. */public class HelloJNI { static { System.loadLibrary("HelloJNI"); } private String name; private HelloJNI(String name) { this.name = name; } public static native HelloJNI getInstance(); public static void main(String[] args) { HelloJNI obj1 = HelloJNI.getInstance(); HelloJNI obj2 = HelloJNI.getInstance(); System.out.println("obj1:--->>name=" + obj1.name); System.out.println("obj2:--->>name=" + obj2.name); } }

然后在C代码中,将jclass和jmethodID对象作为全局变量

#include
#include
#include "com_huachao_java_HelloJNI.h" static jclass cls; static jmethodID midInit; JNIEXPORT jobject JNICALL Java_com_huachao_java_HelloJNI_getInstance (JNIEnv * env,jobject thisObj){ //如果cls为null,则调用FindClass赋值 if(NULL==cls){ cls = (*env)->FindClass(env, "com/huachao/java/HelloJNI"); } if(NULL==cls) return NULL; //如果midInit为NULL,获取构造函数的ID if(NULL==midInit){ midInit = (*env)->GetMethodID(env, cls, "
", "(Ljava/lang/String;)V"); } if (NULL == midInit) return NULL; // 调用构造函数 jstring name=(*env)->NewStringUTF(env, "HuaChao"); jobject newObj = (*env)->NewObject(env, cls, midInit, name); return newObj; }

点击运行,发现报错!!!!!

为什么会出现这样的情况呢?主要是第一次调用FindClass()函数时,能正常获取到com.huachao.java.HelloJNI类对象,并作为全局静态变量存储。但是,在接下来调用中,引用不再有效,虽然此时cls变量不为NULL,主要原因是FindClass()返回的是一个局部引用,当函数执行结束时,引用将变为无效的引用。

为了解决这个问题,可以通过调用NewGlobalRef函数将局部引用转为全局引用。完成转换后,记得将局部引用删除。修改c代码如下:

#include
#include
#include "com_huachao_java_HelloJNI.h" static jclass cls; static jmethodID midInit; JNIEXPORT jobject JNICALL Java_com_huachao_java_HelloJNI_getInstance (JNIEnv * env,jobject thisObj){ //如果cls为null,则调用FindClass赋值 if(NULL==cls){ jclass clsLocal = (*env)->FindClass(env, "com/huachao/java/HelloJNI"); //局部引用转为全局引用 cls = (*env)->NewGlobalRef(env, clsLocal); //删除局部引用 (*env)->DeleteLocalRef(env, clsLocal); } if(NULL==cls) return NULL; //如果midInit为NULL,获取构造函数的ID if(NULL==midInit){ midInit = (*env)->GetMethodID(env, cls, "
", "(Ljava/lang/String;)V"); } if (NULL == midInit) return NULL; // 调用构造函数 jstring name=(*env)->NewStringUTF(env, "HuaChao"); jobject newObj = (*env)->NewObject(env, cls, midInit, name); return newObj; }

运行结果如下:

obj1:--->>name=HuaChaoobj2:--->>name=HuaChao

细心的同学会发现,同为全局引用,为什么jclass全局引用需要将本地引用转为全局引用,而jmethodID对象却不需要?注意,jmethodIDjfieldID并不是jobject对象,无需转换。

转载于:https://www.cnblogs.com/Free-Thinker/p/10608629.html

你可能感兴趣的文章
python爬虫之cookie
查看>>
11.超市收银系统的项目
查看>>
2017年5月29号课堂笔记
查看>>
HDU4247【瞎搞】
查看>>
lightoj 1125【背包·从n个选m个】
查看>>
HDU 1243 反恐训练营(最长公共序列)
查看>>
mysql数据库隔离级别
查看>>
(六)buildroot使用详解
查看>>
chrome修改UserAgent,调试
查看>>
Source Insight4.0 试用。。试用。。试用。。
查看>>
python循环for,range,xrange;while
查看>>
hadoop的节点间的通信
查看>>
HashMap
查看>>
mysql 主从 重新同步
查看>>
论如何制做一个工程APP的测试内容
查看>>
如何通过Java启动linux脚本
查看>>
linux系统调用之网络管理2
查看>>
三种样式表插入方法
查看>>
hdu 2139 Calculate the formula (递推)
查看>>
mac, ios 模拟器
查看>>