简单介绍Java和C/C++交互

1、Java调用C/C++:

   Java代码 [JNITest.java]:

package darcy;

public class JNITest {
  static{
    System.loadLibrary("Hello");    
  }
  
  public native void HelloKitty();
  
  public static void main(String[] args){
    new JNITest().HelloKitty();
  }
}

   编译Class文件,然后使用JDK提供的javah工具生成c++层的头文件,当然也可以手写.

   javah -jni darcy.JNITest

   运行后生成的头文件内容[darcy_JNITest.h]:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class darcy_JNITest */

#ifndef _Included_darcy_JNITest
#define _Included_darcy_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     darcy_JNITest
 * Method:    HelloKitty
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_darcy_JNITest_HelloKitty
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

   这里我们可以看到,Java层的HelloKitty()方法就对应上C++的Java_darcy_JNITest_HelloKitty,从这里我们可以看出Java层对应的C++层方法签名是Java_为前缀+全限定方法名。

   实现这个方法 [darcy_JNITest.cpp]:

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

JNIEXPORT void JNICALL Java_darcy_JNITest_HelloKitty
  (JNIEnv *, jobject){
  printf("hello kitty\n");
}

   下面使用g++编译生成动态链接库libHello.so。对应上Java层调用的System.loadLibrary("Hello");

   从实现的c/c++代码上看到,有个jni.h的头文件需要引进来。该文件位于jdk自带的include文件夹里面,如我的在:/usr/java/jdk1.7.0_45/include这个下面.

   这里需要注意的是jni.h里面需要依赖于一个叫做jni_md.h的文件,但是可能不再在目录下,向我的在/usr/java/jdk1.7.0_45/include/linux下面,如果报相关的编译错误,

   不妨到这里面找一下。

   运行命令,生成.so文件:

   g++ -shared -fPIC -I /usr/java/jdk1.7.0_45/include darcy_JNITest.cpp -o libHello.so

   这里要说明一下关于参数(-fPIC):  这是由于我在编译的时候出现了下面的错误:

 /usr/local/bin/ld: /tmp/ccdq1TYq.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
/tmp/ccdq1TYq.o: error adding symbols: 错误的值

   所以才加上去的,不知道是不是跟之前为了编译linux内核而升级了ld这个东西有关,大家在调用该命令的时候可以先把-fPIC去掉试一下。

   好了,万事具备,我们运行一下Java程序看看:

   java -Djava.library.path=. darcy.JNITest //在=的.表示当前路径

   output:hello kitty

  文件的路径图:

   简单介绍Java和C/C++交互

  2、C/C++调用Java

   Java代码[JNIInvoke.Java]:

package darcy;

public class JNIInvoke{
  public static String invoke(){
     System.out.println("This is from Java!");
     return "Haha";
  }
  
  public static void main(String[] args){
    System.out.println(invoke());
  }
}

  我们将要在c++层调用的是invoke()方法,main只是用来测试.

  第一步是编译代码,生成JNIInvoke.class。

  C++代码[JavaInvoke.cpp]:

#include <iostream>
#include <jni.h>
#include <stdlib.h>
#include <string.h>

using namespace std;

string  JStringToCString (JNIEnv *env, jstring str);  

char* jstringTostring(JNIEnv* env, jstring jstr);

int main(){
    JavaVM *jvm;       /* denotes a Java VM */
    JNIEnv *env;       /* pointer to native method interface */
    JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
    JavaVMOption* options = new JavaVMOption[1];
    options[0].optionString = "-Djava.class.path=.:/usr/java/jdk1.7.0_45/lib";
    vm_args.version = JNI_VERSION_1_6;
    vm_args.nOptions = 1;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = false;
    /* load and initialize a Java VM, return a JNI interface
     * pointer in env */
    JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
    delete options;
    /* invoke the Main.test method using the JNI */
    jclass cls = env->FindClass("darcy/JNIInvoke");
    jmethodID mid = env->GetStaticMethodID(cls, "invoke", "()Ljava/lang/String;");
    jstring result = (jstring)env->CallStaticObjectMethod(cls, mid);
    cout<< jstringTostring(env,result) << endl;  
    /* We are done. */
    jvm->DestroyJavaVM();
    return 0;
}

//把jstring类型转化成c字符串,可不关注该方法
char* jstringTostring(JNIEnv* env, jstring jstr)
{       
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("utf-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0)
{
rtn = (char*)malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
return rtn;
}

  这里有个方法需要注意一下: GetStaticMethodID(), 其中最后一个参数是方法签名(Signature),可以通过javap -s darcy/JNIInvoke看到

   简单介绍Java和C/C++交互

   接下来就是编译C++代码了。

   由上面我们可以看到引进了头文件jni.h,同样的也是在/usr/java/jdk1.7.0_45/include/目录下

   编译指令:

   g++ -g -I/usr/java/jdk1.7.0_45/include/ -L/usr/java/jdk1.7.0_45/jre/lib/amd64/server JavaInvoke.cpp -ljvm

   这里用 -g是为了方便gdb的调试,因为JNI的调用出错之后很难定位错误,所以最后在怀疑出错的地方单布调试一下。-L后面是编译需要用到的库的路径。因为在这里要用到libjvm.so这个库。-ljvm就是定位的库了。如果忘记加进来的话,编译过程会报错:

:对‘JNI_CreateJavaVM’未定义的引用

  最后,为了运行时能找到libjvm.so这个库,要在环境变量中设置参数:

  export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/java/jdk1.7.0_45/jre/lib/amd64/server

  运行: ./a.out

  output:

  This is from Java! //Java层的打印
  Haha //C/C++层的打印

  文件的路径图:

   简单介绍Java和C/C++交互

你可能感兴趣的:(简单介绍Java和C/C++交互)