c/c++调用java
2011年06月30日
一直以来,不喜欢半懂不懂的干事情,读代码的时候发懵了,所以找了下c++调用java的方式方法,作了些删减,摘了下来。
C/C++要调用JAVA程序,必须先加载JAVA虚拟机,由JAVA虚拟机解释执行class文件。为了初始化JAVA虚拟机,JNI提供了一系列的接口函数,通过这些函数方便地加载虚拟机到内存中。
1.加载虚拟机:
函数:jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void args);
参数说明:JavaVM **pvm JAVA虚拟机指针
JNIEnv *env是贯穿整个调用过程的一个参数,因为后面的所有函数都需要这个参数,
2.获取指定对象的类定义:
有两种方法可获得类定义,一是在已知类名的情况使用FindClass来获取;
二是通过对象直接得到类定义GetObjectClass
3.获取要调用的方法:
获得非静态方法:
jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig);
获得静态方法:
jmethodID (JNICALL *GetStaticMethodID)(JNIEnv *env, jclass class, const char *name, const char *sig);
JNIEnv *env初始化是得到的JNI环境;
jclass class前面已获取到的类定义;
const char *name方法名;
const char *sig 签名。方法的定义,我们知道JAVA支持多态,同名方法通过第四个参数来定位得到具体的方法,这就是签名。
4.调用JAVA类方法:
函数:CallObjectMethod(JNIEnv *env, jobject obj, jmethodID mid);
函数:CallStaticObjectMethod((JNIEnv *env, jobject obj, jmethodID mid);
5.获得类属性的定义:
jfieldID (JNICALL *GetFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
静态属性:
jfieldID (JNICALL *GetStaticFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
6.数组处理:
要创建数组首先要知道类型及长度,JNI提供了一系列的数组类型及操作的函数如:
NewIntArray、NewLongArray、NewShortArray、NewFloatArray、NewDoubleArray、NewBooleanArray、NewStringUTF、
NewCharArray、NewByteArray、NewString,访问通过GetBooleanArrayElements、GetIntArrayElements等函数。
7.异常:
由于调用了Java的方法,会产生异常。这些异常在C/C++中无法通过本身的异常处理机制来捕捉到,但可以通过JNI一些函数来获取Java中抛出的异常信息。
8.多线程调用
我们知道JAVA是非常消耗内存的,我们希望在多线程中能共享一个JVM虚拟机,真正消耗大量系统资源的是JAVA虚拟机jvm而不是虚拟机环境env,jvm是允许多个线程访问的,但是虚拟机环境只能被创建它本身的线程所访问,而且每个线程必须创建自己的虚拟机环境env。
这就是发财代码里为什么要Attach和Detach的原因,是为共用java层创建的虚拟机。所以发财代码里没有:
jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void args);
JNI提供了两个函数来提供虚拟机共享:AttachCurrentThread和DetachCurrentThread。便于子线程创建自己的虚拟机环境。
// 类MyTest为了测试JNI使用C/C++调用JAVA
public class MyTest {
// 测试如何访问静态的基本类型属性
//演示对象型属性
public String helloword;
public MyTest() {
this("JNI演示类");
}
//构造函数
public MyTest(String msg) {
System.out.println("构造函数:" + msg);
this.helloword = msg;
}
//该方法演示如何调用动态方
public String HelloWord() {
System.out.println("JAVA-CLASS:MyTest method:HelloWord:" + helloword);
return helloword;
}
//演示异常的捕捉
public void throwExcp() throws IllegalAccessException {
throw new IllegalAccessException("exception occur.");
}
}
用反汇编工具javap在命令行运行:javap -s -p MyTest ,在我自己的windows命令行下看到:
C代码testjava.c:
#include
#include
/*C字符串转JNI字符串*/
jstring stoJstring(JNIEnv* env, const char* pat)
{
jclass strClass = (*env)->FindClass(env,"Ljava/lang/String;");
jmethodID ctorID = (*env)->GetMethodID(env,strClass, "", "([BLjava/lang/String;)V");
jbyteArray bytes = (*env)->NewByteArray(env,strlen(pat));
(*env)->SetByteArrayRegion(env,bytes, 0, strlen(pat), (jbyte*)pat);
jstring encoding = (*env)->NewStringUTF(env,"utf-8");
return (jstring)(*env)->NewObject(env,strClass, ctorID, bytes, encoding);
}
/*JNI字符串转C字符串*/
char* jstringTostring(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = (*env)->FindClass(env,"java/lang/String");
jstring strencode = (*env)->NewStringUTF(env,"utf-8");
jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr, mid, strencode);
jsize alen = (*env)->GetArrayLength(env,barr);
jbyte* ba = (*env)->GetByteArrayElements(env,barr, JNI_FALSE);
if (alen > 0)
{
rtn = (char*)malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
(*env)->ReleaseByteArrayElements(env,barr, ba, 0);
return rtn;
}
int main(int argc, char** argv) {
int res;
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options[3];
/*设置初始化参数*/
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=.";
options[2].optionString = "-verbose:jni"; //用于跟踪运行时的信息
/*版本号设置不能漏*/
vm_args.version=JNI_VERSION_1_2;//jdk版本1.2
vm_args.nOptions = 3;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (res FindClass(env, "MyTest");
if (cls == 0)
{
fprintf(stderr, "FindClass failed\n");
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
return;
}
/*获取构造函数,用于创建对象*/
/***1.1可用""作为构造函数, 1.2用""参数中不能有空格"(Ljava/lang/String;)V"*/
mid = (*env)->GetMethodID(env,cls,"","(Ljava/lang/String;)V");
assert (0 != mid);
fprintf(stderr, "GetMethodID \n");
if (mid == 0)
{
fprintf(stderr, "GetMethodID failed\n");
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
return;
}
fprintf(stderr, "GetMethodID OK\n");
/*创建对象*/
const char str[]="this is a test for c call java";
jobject obj = (*env)->NewObject (env, cls, mid, stoJstring(env, str));
//jobject obj = (*env)->NewObject(env, cls, mid, 0);
/*如果mid为0表示获取方法定义失败*/
fprintf(stderr, "NewObject OK\n");
/*获取方法ID*/
mid=(*env)->GetMethodID(env,cls,"HelloWord","()Ljava/lang/String;");
if (mid == 0)
{
fprintf(stderr, "GetMethodID 'HelloWord' failed\n");
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
return;
}
fprintf(stderr, "GetMethodID 'HelloWord' OK\n");
/*调用动态方法*/
jstring msg = (*env)-> CallObjectMethod(env, obj, mid);
/*
如果该方法是静态的方法那只需要将最后一句代码改为以下写法即可:
jstring msg = (*env)-> CallStaticObjectMethod(env, cls, mid);
*/
/*捕捉异常*/
if ((*env)->ExceptionOccurred(env))
{
(*env)->ExceptionDescribe(env);
return ;
}
/*销毁JAVA虚拟机*/
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
}
编译:
gcc -o testjava testjava.c -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -L${JAVA_HOME}/jre/lib/i386/client -ljvm
运行结果:
GetMethodID
GetMethodID OK
[Dynamic-linking native method java.io.FileOutputStream.writeBytes ... JNI]
MyTest:this is a test for c call java
NewObject OK
GetMethodID 'HelloWord' OK
JAVA-CLASS:MyTest method:HelloWord:this is a test for c call java
Java VM destory.
上面是一个非常简单的例子,你还可以访问类属性,访问静态方法。这样在C中就能跟JAVA里一样调用它的类、方法、访问它的属性
本文摘自CSDN博客:http://blog.csdn.net/xhs_lh04/archive/2008/10/01/3006828.aspx
2011年06月30日
一直以来,不喜欢半懂不懂的干事情,读代码的时候发懵了,所以找了下c++调用java的方式方法,作了些删减,摘了下来。
C/C++要调用JAVA程序,必须先加载JAVA虚拟机,由JAVA虚拟机解释执行class文件。为了初始化JAVA虚拟机,JNI提供了一系列的接口函数,通过这些函数方便地加载虚拟机到内存中。
1.加载虚拟机:
函数:jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void args);
参数说明:JavaVM **pvm JAVA虚拟机指针
JNIEnv *env是贯穿整个调用过程的一个参数,因为后面的所有函数都需要这个参数,
2.获取指定对象的类定义:
有两种方法可获得类定义,一是在已知类名的情况使用FindClass来获取;
二是通过对象直接得到类定义GetObjectClass
3.获取要调用的方法:
获得非静态方法:
jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig);
获得静态方法:
jmethodID (JNICALL *GetStaticMethodID)(JNIEnv *env, jclass class, const char *name, const char *sig);
JNIEnv *env初始化是得到的JNI环境;
jclass class前面已获取到的类定义;
const char *name方法名;
const char *sig 签名。方法的定义,我们知道JAVA支持多态,同名方法通过第四个参数来定位得到具体的方法,这就是签名。
4.调用JAVA类方法:
函数:CallObjectMethod(JNIEnv *env, jobject obj, jmethodID mid);
函数:CallStaticObjectMethod((JNIEnv *env, jobject obj, jmethodID mid);
5.获得类属性的定义:
jfieldID (JNICALL *GetFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
静态属性:
jfieldID (JNICALL *GetStaticFieldID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
6.数组处理:
要创建数组首先要知道类型及长度,JNI提供了一系列的数组类型及操作的函数如:
NewIntArray、NewLongArray、NewShortArray、NewFloatArray、NewDoubleArray、NewBooleanArray、NewStringUTF、
NewCharArray、NewByteArray、NewString,访问通过GetBooleanArrayElements、GetIntArrayElements等函数。
7.异常:
由于调用了Java的方法,会产生异常。这些异常在C/C++中无法通过本身的异常处理机制来捕捉到,但可以通过JNI一些函数来获取Java中抛出的异常信息。
8.多线程调用
我们知道JAVA是非常消耗内存的,我们希望在多线程中能共享一个JVM虚拟机,真正消耗大量系统资源的是JAVA虚拟机jvm而不是虚拟机环境env,jvm是允许多个线程访问的,但是虚拟机环境只能被创建它本身的线程所访问,而且每个线程必须创建自己的虚拟机环境env。
这就是发财代码里为什么要Attach和Detach的原因,是为共用java层创建的虚拟机。所以发财代码里没有:
jint JNI_CreateJavaVM(JavaVM **pvm, void **penv, void args);
JNI提供了两个函数来提供虚拟机共享:AttachCurrentThread和DetachCurrentThread。便于子线程创建自己的虚拟机环境。
// 类MyTest为了测试JNI使用C/C++调用JAVA
public class MyTest {
// 测试如何访问静态的基本类型属性
//演示对象型属性
public String helloword;
public MyTest() {
this("JNI演示类");
}
//构造函数
public MyTest(String msg) {
System.out.println("构造函数:" + msg);
this.helloword = msg;
}
//该方法演示如何调用动态方
public String HelloWord() {
System.out.println("JAVA-CLASS:MyTest method:HelloWord:" + helloword);
return helloword;
}
//演示异常的捕捉
public void throwExcp() throws IllegalAccessException {
throw new IllegalAccessException("exception occur.");
}
}
用反汇编工具javap在命令行运行:javap -s -p MyTest ,在我自己的windows命令行下看到:
C代码testjava.c:
#include
#include
/*C字符串转JNI字符串*/
jstring stoJstring(JNIEnv* env, const char* pat)
{
jclass strClass = (*env)->FindClass(env,"Ljava/lang/String;");
jmethodID ctorID = (*env)->GetMethodID(env,strClass, "", "([BLjava/lang/String;)V");
jbyteArray bytes = (*env)->NewByteArray(env,strlen(pat));
(*env)->SetByteArrayRegion(env,bytes, 0, strlen(pat), (jbyte*)pat);
jstring encoding = (*env)->NewStringUTF(env,"utf-8");
return (jstring)(*env)->NewObject(env,strClass, ctorID, bytes, encoding);
}
/*JNI字符串转C字符串*/
char* jstringTostring(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = (*env)->FindClass(env,"java/lang/String");
jstring strencode = (*env)->NewStringUTF(env,"utf-8");
jmethodID mid = (*env)->GetMethodID(env,clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)(*env)->CallObjectMethod(env,jstr, mid, strencode);
jsize alen = (*env)->GetArrayLength(env,barr);
jbyte* ba = (*env)->GetByteArrayElements(env,barr, JNI_FALSE);
if (alen > 0)
{
rtn = (char*)malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
(*env)->ReleaseByteArrayElements(env,barr, ba, 0);
return rtn;
}
int main(int argc, char** argv) {
int res;
JavaVM *jvm;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options[3];
/*设置初始化参数*/
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=.";
options[2].optionString = "-verbose:jni"; //用于跟踪运行时的信息
/*版本号设置不能漏*/
vm_args.version=JNI_VERSION_1_2;//jdk版本1.2
vm_args.nOptions = 3;
vm_args.options = options;
vm_args.ignoreUnrecognized = JNI_TRUE;
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (res FindClass(env, "MyTest");
if (cls == 0)
{
fprintf(stderr, "FindClass failed\n");
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
return;
}
/*获取构造函数,用于创建对象*/
/***1.1可用""作为构造函数, 1.2用""参数中不能有空格"(Ljava/lang/String;)V"*/
mid = (*env)->GetMethodID(env,cls,"","(Ljava/lang/String;)V");
assert (0 != mid);
fprintf(stderr, "GetMethodID \n");
if (mid == 0)
{
fprintf(stderr, "GetMethodID failed\n");
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
return;
}
fprintf(stderr, "GetMethodID OK\n");
/*创建对象*/
const char str[]="this is a test for c call java";
jobject obj = (*env)->NewObject (env, cls, mid, stoJstring(env, str));
//jobject obj = (*env)->NewObject(env, cls, mid, 0);
/*如果mid为0表示获取方法定义失败*/
fprintf(stderr, "NewObject OK\n");
/*获取方法ID*/
mid=(*env)->GetMethodID(env,cls,"HelloWord","()Ljava/lang/String;");
if (mid == 0)
{
fprintf(stderr, "GetMethodID 'HelloWord' failed\n");
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
return;
}
fprintf(stderr, "GetMethodID 'HelloWord' OK\n");
/*调用动态方法*/
jstring msg = (*env)-> CallObjectMethod(env, obj, mid);
/*
如果该方法是静态的方法那只需要将最后一句代码改为以下写法即可:
jstring msg = (*env)-> CallStaticObjectMethod(env, cls, mid);
*/
/*捕捉异常*/
if ((*env)->ExceptionOccurred(env))
{
(*env)->ExceptionDescribe(env);
return ;
}
/*销毁JAVA虚拟机*/
(*jvm)->DestroyJavaVM(jvm);
fprintf(stdout, "Java VM destory.\n");
}
编译:
gcc -o testjava testjava.c -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux -L${JAVA_HOME}/jre/lib/i386/client -ljvm
运行结果:
GetMethodID
GetMethodID OK
[Dynamic-linking native method java.io.FileOutputStream.writeBytes ... JNI]
MyTest:this is a test for c call java
NewObject OK
GetMethodID 'HelloWord' OK
JAVA-CLASS:MyTest method:HelloWord:this is a test for c call java
Java VM destory.
上面是一个非常简单的例子,你还可以访问类属性,访问静态方法。这样在C中就能跟JAVA里一样调用它的类、方法、访问它的属性
本文摘自CSDN博客:http://blog.csdn.net/xhs_lh04/archive/2008/10/01/3006828.aspx