首先来看一下C/C++中怎么创建Java对象:在JNIEnv中有两种方法是用来创建Java对象的:
第一种方法:
jobject NewObject(jclass clazz , jmethodID methodID, ....):
参数解释:
clazz:这个很简单,就是需要创建的Java对象的Class对象
methodID:这个是传递一个方法的ID,想一想Java对象在创建的时候,需要执行什么方法呢?对,没错那就是构造方法
第三个参数:是构造函数需要传入的参数值(默认的构造方法是不需要传入这个参数的)
所以我们在创建Java对象的时候之前要做的工作就是要获取这个对象的class对象,然后再获取该对象的构造方法,想要获取方法的id,就需要方法的签名,因为构造方法没有返回值,所以我们认为类的默认构造方法的返回值类型的签名始终是“()V”(因为默认的构造方法是没有参数的),方法的名称始终为“<init>”
例子:在C++中构造Java中的Date对象,并且调用它的getTime()方法打印当前时间
Java中的代码不需要改变,主要是C++代码中的改写:
-
#include<iostream.h>
-
#include"com_jni_demo_JNIDemo.h"
-
-
JNIEXPORTvoidJNICALLJava_com_jni_demo_JNIDemo_sayHello(JNIEnv*env,jobjectobj)
-
{
-
-
jclassclazz_date=env->FindClass("java/util/Date");
-
-
jmethodIDmid_date=env->GetMethodID(clazz_date,"<init>","()V");
-
-
jobjectnow=env->NewObject(clazz_date,mid_date);
-
-
jmethodIDmid_date_getTime=env->GetMethodID(clazz_date,"getTime","()J");
-
-
jlongtime=env->CallLongMethod(now,mid_date_getTime);
-
-
printf("%I64d",time);
-
}
编译成.dll文件,在Eclipse中运行结果如下:
这个输出的时间没有进行格式化处理。
第二种方法:
创建一个对象:就是用AllocObject
使用函数AllocObject可以根据传入的jclass创建一个Java对象,但是他的状态时非初始化的,在是哟个这个对象之前绝对要用CallNonvirtualVoidMethod来调用该jclass的构造函数,这样就可以延迟构造函数的调用,这一部分用的很少,只做简单的说明:
Java中的代码不做任何修改,C++代码中修改成如下:
-
#include<iostream.h>
-
#include"com_jni_demo_JNIDemo.h"
-
-
JNIEXPORTvoidJNICALLJava_com_jni_demo_JNIDemo_sayHello(JNIEnv*env,jobjectobj)
-
{
-
-
jclassclazz_date=env->FindClass("java/util/Date");
-
jmethodIDmethodID_str=env->GetMethodID(clazz_date,"<init>","()V");
-
jobjectnow=env->AllocObject(clazz_date);
-
-
env->CallNonvirtualVoidMethod(now,clazz_date,methodID_str);
-
-
jmethodIDmid_date_getTime=env->GetMethodID(clazz_date,"getTime","()J");
-
-
jlongtime=env->CallLongMethod(now,mid_date_getTime);
-
-
printf("%I64d",time);
-
}
这种方式是很少用的!
下面来看一下C/C++中如何操作Java中的字符串
首先来了解一下Java和C/C++中字符串的却别:
(1). 在Java中,使用的字符串String对象是Unicode(UTF-16)码,即每个字符不论是中文还是英文还是符号,一个字符总是占两个字节
(2). Java通过JNI接口可以将Java的字符串转换到C/C++中的宽字符串(wchar_t *),或是传回一个UTF-8的字符串(char*)到C/C++,反过来,C/C++可以通过一个宽字符串,或是一个UTF-8编码的字符串来创建一个Java端的String对象
接下来看一下JNIEnv中的一些C++方法:
1.获取字符串的长度:
jsize GetStringLength(jstring j_msg);
参数:j_msg:是一个jstring对象
2.将jstring对象拷贝到const jchar*指针字符串
//这个方法是:拷贝Java字符串并以UTF-8编码传入jstr
env->GetStringRegion(jstring j_msg , jsize start , jsize len , jchar* jstr);
//这个方法是:拷贝Java字符串并以UTF-16编码传入jstr
env->GetStringUTFRegion(jstring j_msg , jsize start , jsize len , char* jstr);
在Java1.2出来的函数,这个函数的动作时把Java字符串的内容直接拷贝到C/C++的字符串数组中,在呼叫这个函数之前必须有一个C/C++分配出来的字符串(具体看下面的例子),然后传入到这个函数中进行字符串的拷贝
由于C/C++中分配内存开销相对小,而且Java中的String内容拷贝的开销可以忽略,更好的一点是此函数不分配内存,不会抛出OutOfMemoryError异常
参数:j_msg:是一个jstring对象,start是拷贝字符串的开始位置,len是拷贝字符串的长度,jstr是目标指针字符串
3.生成一个jstring对象
jobject NewString(const jchar* jstr , int size);
参数:jstr是字符串指针,size是字符串长度
这个方法可以认为是将字符串指针jstr转化成字符串对象jstring
4.将jstring对象转化成const jchar*字符串指针
(1) const* jchar* GetStringChars(jstring j_msg , jboolean* copied);
返回一个UTF-16编码的宽字符串(jchar*)
参数:
j_msg是字符串对象
copied是指传入的是一个jboolean指针,用来标识是否对Java的String对象进行了拷贝的,如果传入的这个jboolean指针不是NULL,则它会给该指针所指向的内存传入JNI_TRUE或JNI_FALSE标识是否进行了拷贝,传入NULL表示不关心是否拷贝字符串,它就不会给jboolean* 指向的内存赋值
其对应的释放内存指针的方法:
ReleaseStringChars(jstring j_msg , const jchar* jstr);
参数:j_msg是jstring对象,jstr是字符串指针
在这里还有一个方法就是:
(2) const char* GetStringUTFChars(jstring str , jboolean* copied)
这个方法是可以取得UTF-8编码的字符串(char*)
参数的含义和GetStringChars方法是一样的
这个方法也有对应的一个释放内存的方法:
ReleaseStringUTFChars(jstring jstr , const char*str);
参数的含义和上面的ReleaseStringChars方法的参数的含义是一样的
需要注意的是:
这两个函数分别都会有两个不同的动作:
(1) 开辟一个新内存,然后在Java中的String拷贝到这个内存中,然后返回指向这个内存地址的指针
(2) 直接返回指向Java中String的内存的指针,这个时候千万不要改变这个内存的内容,这个将会破坏String在Java中始终是常量的这个原则
5.将jstring对象转化成const jchar*字符串指针
const jchar* GetStringCritical(jstring j_msg , jboolean* copied);
参数:j_msg是字符串对象,copied上面的方法已经做了解释了,这里就不多说了
这个方法的作用是为了增加直接传回指向Java字符串的指针的可能性(而不是拷贝),JDK1.2出来了新的函数GetStringCritical/ReleaseStringCritical,
在GetStringCritical/ReleaseStringCritical之间是一个关键区,在这个关键区域之间不能呼叫JNI的其他函数和会造成当前线程中断,或是会让当前线程等待的任何本地代码,否则将造成关键区代码执行期间垃圾回收器停止运作,任何触发垃圾回收器的线程也会暂停,其他的触发垃圾回收器的线程不能前进直到当前线程结束而激活垃圾回收器
在关键区域中千万不要出现中断操作,或是在JVM中分配任何新对象,否则会造成JVM死锁
虽说这个函数会增加直接传回指向Java字符串的指针的可能性,不过还是会根据情况传回拷贝过的字符串
不支持GetStringUTFCritical,没有这样的函数,由于Java字符串用的是UTF-16,要转成UTF-8编码的字符串始终需要进行一次拷贝,所以没有这样的函
这个方法和第四个方法是一样的功能
其对应的释放内存指针的方法:
env->ReleaseStringCritical(jstring j_msg , const jchar* jstr);
下面来看一下实例:在Java中定义一个String属性,通过控制台输入值,然后定义一个本地方法callCppFunction,在C++中这个方法的实现就是:获取到Java中这个字符串属性,将其进行倒序操作然后,然后再Java中输出:
先来看一下Java中的代码:
-
packagecom.jni.demo;
-
-
importjava.io.BufferedReader;
-
importjava.io.InputStreamReader;
-
-
publicclassJNIDemo{
-
-
-
publicnativevoidcallCppFunction();
-
-
publicStringmsg=null;
-
publicstaticvoidmain(String[]args)throwsException{
-
-
System.loadLibrary("JNIDemo");
-
-
BufferedReaderreader=newBufferedReader(newInputStreamReader(System.in));
-
Stringstr=reader.readLine();
-
JNIDemojniDemo=newJNIDemo();
-
jniDemo.msg=str;
-
jniDemo.callCppFunction();
-
System.out.println(jniDemo.msg);
-
-
}
-
-
}
在来看一下C++代码:
-
#include<iostream>
-
#include"com_jni_demo_JNIDemo.h"
-
#include"windows.h"
-
#include<string>
-
#include<algorithm>
-
usingnamespacestd;
-
-
JNIEXPORTvoidJNICALLJava_com_jni_demo_JNIDemo_callCppFunction(JNIEnv*env,jobjectobj)
-
{
-
-
jfieldIDfid_msg=env->GetFieldID(env->GetObjectClass(obj),"msg","Ljava/lang/String;");
-
-
jstringj_msg=(jstring)env->GetObjectField(obj,fid_msg);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
jsizelen=env->GetStringLength(j_msg);
-
-
jchar*jstr=newjchar[len+1];
-
-
jstr[len]=L'\0';
-
-
env->GetStringRegion(j_msg,0,len,jstr);
-
-
wstringwstr((constwchar_t*)jstr);
-
-
delete[]jstr;
-
-
-
-
reverse(wstr.begin(),wstr.end());
-
-
jstringj_new_str=env->NewString((constjchar*)wstr.c_str(),(jint)wstr.size());
-
-
env->SetObjectField(obj,fid_msg,j_new_str);
-
}
这里使用了三种方式实现功能。这里要注意的是还有一个方法就是将const jchar*转换成wstring,因为reverse方法接受的参数是wstring。在Eclipse中运行结果如下:
到这里这一篇的内容就讲到着了,后面的内容更精彩!
分享到:
相关推荐
JNI开发Java和C/C++互相传递List集合, 可以参考: Java从C/C++获取List集合对象:https://blog.csdn.net/niuba123456/article/details/80994166 Java传递List集合对象到C/C++ ...
C/C++中访问Java方法 C/C++中访问Java父类的方法 C/C++中访问/修改Java变量 Java中访问C/C++方法 Java中访问/修改C/C++变量 动态方式实现: C/C++中访问Java方法 C/C++中访问Java父类的方法 C/C++中访问/修改Java...
使用C/C++实现Java的Native方法接口(JNI)/ JNI编程(C/C++) 代码实例
java 调用 dll 的方法,即JNI的使用,demo中有get()/set()方法,操作步骤详细,即使是没用过java的程序员按照步骤依然可以成功。
NULL 博文链接:https://conkeyn.iteye.com/blog/1597188
Java通过JNI和c++对象数据的传递和对象的返回
II. java c/cpp互相调用实例(姊妹篇之一)——java调用c/cpp 4 一 先制作一个系统中有的DLL文件(cpp给出的sdk接口) 4 二 JNI 7 1、 编写java文件 7 2、 生成.h头文件 8 3、 用c/cpp实现这个头文件 9 三 测试 10 ...
利用JNI技术实现Java中调用C++编写的函数库示例程序源码,并附上参考JNI文档。 详情见本人博客:Java学习之通过JNI调用C/C++编写的dll链接库(图文教程)(http://write.blog.csdn.net/postlist)
Android JNI/NDK开发(2)JNI实现C/C++与Android/JAVA相互调用 http://blog.csdn.net/u014702653/article/details/71141423
通过jni完成java调用c/c++,包含c/c++生成Dll动态库
这是我用于测试,主要用于jni创建java对象病操作
我们知道,在C层有*.SO,它有全局变量,不要将Java或C++对象参考储存于C层的全局变量,把动态的对象指针放在全局变量是不好的。这样可以增加C函数的稳定度,包括它的通用性,可适应于更多更多的环境里使用,表示它...
JNI开发Java调用C传递int、String、Array类型参数; 详情参考: int类型: (https://blog.csdn.net/niuba123456/article/details/80959892) String类型(https://blog.csdn.net/niuba123456/article/details/80977247);...
在VS2013中使用JNI进行JAVA和C++之间的互调,这两个文件只是项目中的一部分,仅供参考
jni对象获取,使用jni从c++里获取Java对象
NDK开发时,C/C++调用Java的函数的一些案例; 传递int类型参数: https://blog.csdn.net/niuba123456/article/details/80978500 传递String类型参数: https://blog.csdn.net/niuba123456/article/details/80978916 ...
cocos2d-x 通过JNI实现c/c++和Android的java层函数互调-源码 详情请移步到:http://codingnow.cn/program/992.html
Android JNI中C++层与Java层的对象交互实例代码详细介绍。Android JNI中C++层与Java层的对象交互实例代码详细介绍。Android JNI中C++层与Java层的对象交互实例代码详细介绍。Android JNI中C++层与Java层的对象交互...
Chap5:使用JNI技术实现java程序调用第三方dll(c/c++)文件的功能... 47 Chap9:如何编写jni方法(转载)... 55 1、实例一:在jni中调用标准c中自带的函数printf(): 57 2、实例二、调用c 语言用户定义的函数... 58 ...
http://blog.csdn.net/csdn49532/article/details/50624627 配套代码,使用JAVA和JNI二种方法动态加载dex,调用dex中的方法,对android APK进行加固,防止APK被破解。