`
songry
  • 浏览: 84558 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

使用c通过jni调用java

阅读更多

编译环境:

fedora16

gcc (GCC) 4.6.3 20120306 (Red Hat 4.6.3-2)

java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b04)
Java HotSpot(TM) Server VM (build 20.6-b01, mixed mode)

 

准备工作:

    首先需要安装jdk和gcc(或者其他c编译器也可以)并配置相应的环境变量(可自行在网上搜索,这个资料很多的),配好之后在命令行运行gcc --version和java -version出现版本号就代表安装配置成功了:

[roysong@roysong c]$ gcc --version
gcc (GCC) 4.6.3 20120306 (Red Hat 4.6.3-2)
Copyright © 2011 Free Software Foundation, Inc.
本程序是自由软件;请参看源代码的版权声明。本软件没有任何担保;
包括没有适销性和某一专用目的下的适用性担保。
[roysong@roysong c]$ java -version
java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b04)
Java HotSpot(TM) Server VM (build 20.6-b01, mixed mode)

 

     然后需要将jdk中的虚拟机库配置到环境路径中,我是在/etc/ld.so.conf.d/下面新建了一个jni-i386.conf文件,内容如下:

/usr/java/jdk1.6.0_31/jre/lib/i386/client

    然后运行一下ldconfig –v,让配置文件起作用,也可以直接编辑/etc/ld.so.conf文件,将这个路径加入进去.这个

路径的含义就是$JAVA_HOME/jre/lib/i386/client,其中$JAVA_HOME换成你自己的jdk安装路径就可以了.这是

为了找到libjvm.so.因为我是32位的机器,所以路径中是i386,我估计64位的机器这个目录是不一样的,根据

libjvm.so文件的路径自行替换即可.

 

 程序编写和编译:

    程序就分成了c的部分和java的部分,首先我们先看看java的部分,我是把在同一目录下新建了两个文件夹分别叫

c和java,作为c程序的路径和java的classpath,然后在java文件夹下面新建包com.test.base64,java程序的目录

结构即:java/com/test/base64.接着,我们在base64目录下面新建一个Hello.java:

package com.test.base64;

public class Hello{
	public String hello (String name){
		return "hello,"+name;	
	}
}

    结构非常简单,不过有参数有返回值就可以代表大部分情况了.然后我们使用javac对这个文件进行编译以产生

一个class文件:

[roysong@roysong java]$ cd com/test/base64/
[roysong@roysong base64]$ javac Hello.java
[roysong@roysong base64]$ ls
 Hello.class  Hello.java 

   编译成功后,在base64目录下会出现Hello.class这个文件.

   然后我们回到c文件夹,新建一个c源文件,我的是cfjIns.c,开始编写c的程序:

#include <stdio.h>
#include <jni.h>

/**
*初始化jvm
*/
JNIEnv* create_vm() {
        JavaVM* jvm;
        JNIEnv* env;
        JavaVMInitArgs args;
        JavaVMOption options[1];
        args.version = JNI_VERSION_1_6;
        args.nOptions = 1;
        options[0].optionString = "-Djava.class.path=../java";
        args.options = options;
        args.ignoreUnrecognized = JNI_FALSE;
        JNI_CreateJavaVM(&jvm, (void **)&env, &args);
        return env;
}

 

    这是cfjIns.c的第一部分,头文件声明

#include <jni.h>

    非常重要,这是引用的$JAVA_HOME/include/jni.h,一会儿我们在编译时会加上这个路径.

    其次就是

 args.version = JNI_VERSION_1_6;

     这是jni的版本信息,需要跟你自己的jdk中jni版本对应,jdk1.6和jdk1.7的jni版本都是上面这个.

options[0].optionString = "-Djava.class.path=../java";

     这个参数是指明你自己java程序的类路径(classpath),因为我的c文件夹和java文件夹在同一目录下,所以我用了

一个相对路径来指明classpath.

 

    cfjIns.c中获取类定义的函数:

/**
* 根据全限定类名来获取类的定义
*/
jclass create_class(JNIEnv* env,char *className){
        jclass cls = (*env)->FindClass(env,className);
        if(cls == 0){
                printf("class-[%s] find error\n",className);
                return;
        }
        return cls;
}

    这个函数有两个参数,第一个就是我们上面通过create_vm函数创建的jvm环境,第二个是全限定的类名字符串,

比如:"com/test/base64/Hello",返回值即对应的类.

 

    cfjIns.c中获取类实例的函数:

/**
*通过无参构造函数来获取对应类的实例
*/
jobject getInstance(JNIEnv* env, jclass obj_class)
{ 
        jmethodID construction_id = (*env)->GetMethodID(env,obj_class, "<init>", "()V");
        jobject obj = (*env)->NewObject(env,obj_class, construction_id);
        if(obj == 0){
                printf("java class instance failed\n");
                return;
        }
        return obj; 
}

    这个函数的第一个参数是jvm环境,第二个是通过上面的create_class函数获取的类定义.

 

    cfjIns.c中获取方法定义的函数:

/**
* 根据类\方法名\返回值\参数获取类中对应的方法定义
*/
jmethodID get_method(JNIEnv* env,jclass cls,char *methodName,char *key){
        jmethodID mid = (*env)->GetMethodID(env,cls,methodName,key);
        if(mid == 0){
                printf("method-%s is not found\n",methodName);
                return;
        }
        return mid;
}

    这儿我们需要注意的是最后一个参数字符串key,这个是方法签名,用于指明方法的参数和返回值类型,格式是

"(参数类型声明)返回值类型声明".类型声明的值参照下面的对应表:

Java类型      对应的签名
boolean Z
byte B
char C
shrot S
int I
long L
float F
double D
void V
Object L用/分割包的完整类名;  Ljava/lang/String;
Array [签名       [I       [Ljava/lang/String;

 

    举个例子,如果方法的参数是int,返回值是void,那么方法的签名就是"(I)V";如果方法的参数是String,返回值

也是String,则方法的签名就是"(Ljava/lang/String;)Ljava/lang/String;",注意,如果是引用类型,类名后面必须

带有一个分号.

    我们也可以通过javap来查看类中方法参数和返回值的签名:

[roysong@roysong c]$ cd ../java/com/test/base64/
[roysong@roysong base64]$ javap -s -private Hello
Compiled from "Hello.java"
public class com.test.base64.Hello extends java.lang.Object{
public com.test.base64.Hello();
  Signature: ()V    //构造函数的签名
public java.lang.String hello(java.lang.String);
  Signature: (Ljava/lang/String;)Ljava/lang/String; //hello方法的签名
}
 

 

    然后是一个将c语言中的字符串转换为java中String的工具函数:

/**
* 转换c中的字符串为java.lang.String,这个方法是从网上找到的,感谢原作者天末凉风



*/
jstring stoJstring(JNIEnv* env, const char* pat)
{
        jclass strClass = (*env)->FindClass(env,"Ljava/lang/String;");
        jmethodID ctorID = (*env)->GetMethodID(env,strClass, "<init>", "([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);
}

    有了上面的例子,这个函数就很好理解了,首先获取到java.lang.String的类定义,然后获取构造函数,然后将

c中的字符串转化为字节流并设置编码格式,最后产生一个新的java.lang.String对象并返回.

 

   这下我们就准备齐全了,开始编写调用函数:

void invoke(){
        JNIEnv* env = create_vm(); //初始化java虚拟机
        jclass cls = create_class(env,"com/test/base64/Hello");//根据类名找到对应的类
        jobject obj = getInstance(env,cls);//然后根据类获取对应的实例
        jmethodID hello = get_method(env,cls,"hello","(Ljava/lang/String;)Ljava/lang/String;");//根据类\方法名和签名获取到对应的方法
        jstring name_str =  (*env)->CallObjectMethod(env,obj,hello,stoJstring(env,"a"));//传入参数调用方法
        const char* pname = (*env)->GetStringUTFChars(env,name_str, NULL);//将返回的java字符串转换为c字符串
        printf("the result is:%s\n",pname);//打印出调用的结果
}

    调用函数编写完成后,用一个main函数来运行一下查看结果:

int main(int argc, char **argv) {
        invoke();
}
 

     至此,c的源代码cfjIns.c就编写完成了,我们开始编译cfjIns.c:

[roysong@roysong c]$ gcc -o cfj cfj.c -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -L$JAVA_HOME/jre/lib/i386/client -ljvm 
[roysong@roysong c]$ ls
cfjIns  cfjIns.c

     编译命令中我们使用-I选项加入了两个头文件路径,$JAVA_HOME/include这个是为了引用到jni.h,而

$JAVA_HOME/include/linux这个为了引用到jni_md.h,因为jni.h中有对jni_md.h的引用.我的操作系统是fedora,所以

jni_md.h在$JAVA_HOME/include的linux文件夹下面,其他操作系统可能路径不同,找到jni_md.h文件的路径进行

替换即可.使用-L选项是为了引用到java虚拟机的库文件libjvm.so,-l选项(注意,这是小写的L,而不是大写的i)是声明

具体引用的库jvm.如果libjvm.so文件的路径与我的不同,找到libjvm.so文件的路径进行替换即可.

    编译成功后,目录下面会出现一个可运行的cfj文件,如果一切顺利,我们运行它就可以得到预期的结果:

[roysong@roysong c]$ ./cfjIns
hello,a

    显示正常,调用完成

 

分享到:
评论

相关推荐

    JAVA通过JNI调用C#dll的整个项目工程

    Java通过JNI调用C# DLL是一个跨平台、跨语言的技术实践,主要应用于需要利用Java的稳定性和C#的高性能场景。JNI(Java Native Interface)是Java平台标准的一部分,它允许Java代码和其他语言写的代码进行交互。C# ...

    linux C JNI调用java

    本资源详细介绍了如何在Linux环境中通过C语言使用JNI调用Java类的函数,以下是对这个主题的详细阐述。 首先,要进行C语言调用Java类,我们需要确保有Java Development Kit (JDK) 安装并正确配置。JDK包含了一系列...

    Delphi10.3 中通过JNI调用 Java 函数

    总结来说,Delphi 10.3通过JNI调用Java函数的过程涉及以下步骤: 1. 创建C++ DLL项目,引入JNI头文件。 2. 定义JNI函数,实现Delphi和Java之间的交互逻辑。 3. 在Delphi程序中加载DLL,获取JNI函数的指针。 4. 使用...

    visual studio 2019下C++通过JNI调用JAVA代码

    在本文中,我们将深入探讨如何在Visual Studio 2019环境下使用C++通过Java Native Interface (JNI)来调用Java代码。JNI是Java平台的一部分,它为Java应用程序提供了与本地代码交互的能力,使得开发者可以将Java应用...

    delphi通过JNI调用JAVA函数

    要实现Delphi通过JNI调用Java函数,我们需要以下步骤: 1. **设置环境**:确保Java Development Kit (JDK) 已经安装,因为JNI的头文件和库文件都在JDK中。在Delphi项目中,我们需要配置包含路径以找到这些头文件,...

    GMSSL的java调用(JNI库和调用实例).zip

    - **JNI动态库**:这是由C或C++编写的库,实现了GMSSL的原生接口,供Java通过JNI调用。库可能有多个版本,针对不同的操作系统和处理器架构。 - **Java调用示例**:这些示例代码展示了如何在Java中加载和使用JNI库,...

    JNI中C调用Java方法的实例

    在本实例中,我们将深入探讨如何使用纯C语言通过JNI调用Java对象的方法。这个例子特别适合那些需要利用C/C++的高效性能,同时又需要与Java应用程序集成的开发者。 首先,我们需要理解JNI的基本概念。JNI提供了一套...

    QTC++Android使用JNI调用java方法实现串口通信

    串口通信java包

    Android通过JNI调用.so动态库

    Android 通过 JNI(Java Native Interface)调用.so 动态库是 Android 开发中的一种常用技术。JNI 是一种允许 Java 代码与 native 代码之间进行交互的接口。通过 JNI,我们可以在 Android 项目中调用.so 动态库中的 ...

    jni.zip_C++ JNI_JNI 调用java_c调用java_jni_jni jdk1

    通过以上步骤,我们可以实现C++通过JNI调用Java类的功能。这个过程涉及到了Java和C++的混合编程,需要对两种语言有深入的理解,并且熟悉JNI提供的接口。在实际应用中,开发者还需要考虑多线程、内存管理、错误处理等...

    Java通过JNI调用C语言函数库的方法.zip

    本资料"Java通过JNI调用C语言函数库的方法.zip"提供了一个名为"JavaJNIStudy-master"的项目,下面将详细介绍这一过程的关键知识点。 1. **JNI简介** JNI是Java平台的标准接口,它使得Java应用程序可以与本地代码...

    android JNI C 调用Java

    Android JNI(Java Native Interface)是Android系统提供的一种机制,它允许Java代码调用C/C++原生代码,同时也允许C/C++代码调用Java的方法。JNI在开发高性能、低级硬件交互、使用现有C库或者优化性能的关键部分时...

    JNI,java调用c Demo

    通过查看和运行这个项目,你可以更深入地理解JNI的工作流程,以及如何在实际的Android应用中使用JNI调用本地代码。 JNI的使用虽然增加了代码的复杂性,但它提供了与Java平台交互的强大能力。例如,它可以用于优化...

    jni C调用java示例

    本示例将讲解如何在Android Studio 2.3.3版本下,通过C语言调用Java方法。 首先,理解JNI的基本结构。在Java端,我们需要定义一个本地方法,即使用`native`关键字声明的方法。例如: ```java public class ...

    JNI与Java方法的相互调用

    JNI接口定义了一组函数,Java代码可以通过这些函数调用本地方法,反之,本地方法也可以通过这些函数调用Java方法。在Java代码中,我们需要使用`System.loadLibrary("name")`来加载本地库,并声明`native`关键字标记...

    Java JNI调用IC卡读卡器

    通过以上步骤,你就可以在Java应用程序中利用JNI调用IC卡读卡器的DLL,实现读取和写入IC卡的功能。注意,调用DLL可能会涉及线程安全、错误处理以及资源管理等问题,所以在实际应用中需要仔细考虑这些因素。此外,...

    Android JNI native调用 java层demo TESTJNI.zip

    本文将深入探讨在Android中如何使用JNI,特别是如何从C++ native代码调用Java层的类、方法、属性以及接口,同时涉及线程回调接口的实现。 首先,我们需要了解JNI的基本结构。一个典型的JNI应用会包含Java源文件、C/...

    使用JNA替代JNI调用DLL,并解决内存溢出问题

    ### 使用JNA替代JNI调用DLL,并解决内存溢出问题 #### 问题背景 在项目的开发过程中,常常遇到需要处理二进制流数据并对其进行解析处理的情况。这种情况下,如果上层应用平台采用的是Java开发,而底层算法或数据...

    Android使用JNI调用Python so解释器

    总结来说,"Android使用JNI调用Python so解释器"涉及到Android NDK开发,JNI接口设计,Python解释器的Android移植,以及跨语言通信等多个技术层面。这种技术虽然复杂,但能充分利用Python的灵活性和Android的广泛...

Global site tag (gtag.js) - Google Analytics