簡單介紹Java和C/C++交互

一、Java調用C/C++:

   Java代碼 [JNITest.java]: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++層的頭文件,固然也能夠手寫.linux

   javah -jni darcy.JNITestios

   運行後生成的頭文件內容[darcy_JNITest.h]:c++

/* 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_爲前綴+全限定方法名。jvm

   實現這個方法 [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這個下面.spa

   這裏須要注意的是jni.h裏面須要依賴於一個叫作jni_md.h的文件,可是可能再也不在目錄下,向個人在/usr/java/jdk1.7.0_45/include/linux下面,若是報相關的編譯錯誤,調試

   不妨到這裏面找一下。code

   運行命令,生成.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

  文件的路徑圖:

  

  二、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看到

  

   接下來就是編譯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++層的打印

  文件的路徑圖:

  

相關文章
相關標籤/搜索