JNI

實驗於JDK8

一、新建測試Java類  JNIT

public class JNIT {   
    static { 
        System.loadLibrary("JNIT"); 
    }
    public static native String hello(String msg);       
    public static void  main(String[] args) {    
        String str= hello("Hello, c++!" );
        System.out.print("java get c++ Str:"+str);
    }
}
 

 

二、在JNIT.java文件同目錄下執行 javac JNIT.java   在同目錄下生成 JNIT.class  文件

三、在JNIT.class同目錄下執行 javah -classpath . -jni  JNIT    ,生成 JNIT.h文件 內容以下

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

#ifndef _Included_JNIT
#define _Included_JNIT
#ifdef __cplusplus
extern "C" {
#endif
    /*
     * Class:     JNIT
     * Method:    hello
     * Signature: (Ljava/lang/String;)Ljava/lang/String;
     */
    JNIEXPORT jstring JNICALL Java_JNIT_hello
    (JNIEnv *, jclass, jstring);

#ifdef __cplusplus
}
#endif
#endif

 

注:
文件中的 Java_JNIT_hello 說明是Java中的JNIT類的hello方法
文件中的 #include<jni.h>等 <> 符號在命令行打包動態庫時會打包失敗 須要改成 「jni.h」java

生成的文件中須要導入的jni.h 和 jni_md.hJAVAHOME/jdk/includeJAVAHOME/jdk/include/win32 目錄下,你能夠選擇直接拷貝或後期配置添加(注意32和64位區分)linux

四、在JNIT.h相同目錄下新建JNIT.cpp ,實現JNIT.h 中方法

// dllmain.cpp : 定義 DLL 應用程序的入口點。
// pch.cpp: 與預編譯標頭對應的源文件

#include "JNIT.h"
// 當使用預編譯的頭時,須要使用此源文件,編譯才能成功。
#include <iostream>
using namespace std;
JNIEXPORT jstring JNICALL Java_JNIT_hello
(JNIEnv * jEnv, jclass jcls, jstring jstr) {
    const char *c_str = NULL;
    char buff[128] = { 0 };
    c_str = (jEnv)->GetStringUTFChars(jstr,false);
    if (c_str == NULL)
    {
        printf("out of memory.\n");
        
    }
    cout << "c++ get Java Str: "<< c_str << endl;

//    (jEnv)->ReleaseStringUTFChars( jstr, c_str);
//printf("c++ get Java Str:%s\n", c_str);

    return (jEnv)->NewStringUTF("hello java");

}

 

五、準備gcc編譯環境

這裏gcc的環境有32位與64位的區分 否則運行會有錯誤Exception in thread "main" java.lang.UnsatisfiedLinkError: D:\eclipsejee\workspace\JNIT\src\JNIT.dll: Can't load IA 32-bit .dll on a AMD 64-bit platformios

六、使用VS2017的命令行編譯

 cl -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -LD HelloWorld.c -FeHelloWorld.dll   
參數選項說明:
    -I :和 mac os x 同樣,包含編譯 JNI 必要的頭文件
    -LD:標識將指定的文件編譯成動態連接庫
    -Fe:指定編譯後生成的動態連接庫的路徑及文件名

六、使用VS2017新建dll項目, 編譯打包

(1)新建dll項目c++

(2)將寫好的JNIT.cpp粘貼內容粘貼到 dllMain.cpp 中  這裏能夠對dllMain.cpp 修更名稱,將JNIT.h粘貼進項目目錄。app

(3)右鍵項目》屬性》c/c++》常規》附加包含目錄,將jdk/include和jdk/include/win32添加進去,從而引入jni.h和jni_md.heclipse

(4)去除編譯器默認對pch文件的預編譯,右鍵項目》屬性》c/c++》預編譯頭》預編譯頭》不使用預編譯頭函數

(5)運行項目,會在控制檯輸出dll文件路徑測試

六、使用MinGW 在window命令行,執行編譯打包  , 會在當前目錄下生成 .dll 文件

g++ -I%JAVA_HOME%\include -I%JAVA_HOME%\include\win32 -shared -Wl,–kill-at -s -o lib.dll JNIT.cpp
解釋一下:
-I(大寫字母I,include的意思)是加入本身的庫,也就是告訴編譯器jni.h的位置。固然不加這個參數也能夠,本身把jni.h和jni_md.h文件複製出來和Test.c放一塊兒,另外include改成」」
-shared表示編譯成.dll庫文件
-s參數能夠大幅減少.dll文件的大小,不加也能夠
-o表示目標文件名,不加也能夠,會有默認名,但要本身改爲java中導入庫的名字,這裏是lib
-Wl,–kill-at  防止編譯後的函數名被自動加上@符號,並取消警告。(是小寫字母L,不是數字1)

當你把jni.h 和jni_md.h 粘貼到當前目錄下可使用如下命令
g++ -shared -Wl,-kill-at -s -o lib.dll JNIT.cpp
g++ -s -o JNIT.dll JNIT.cpp

六、linux下g++編譯打包,會在當前目錄下生成 .so文件    

g++ -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -fPIC -shared -o JNIT.so  
參數說明:
    -I: 包含編譯JNI必要的頭文件
    -fPIC: 編譯成與位置無關的獨立代碼
    -shared:編譯成動態庫
    -o: 指定編譯後動態庫生成的路徑和文件名

當你把jni.h 和jni_md.h 粘貼到當前目錄下可使用如下命令
g++ -shared -Wl,-kill-at -s -o lib.dll JNIT.cpp
g++ JNIT.cpp -fPIC -shared -o JNIT.so

七、將生成的.so(linux) 或.dll(window)複製到 以前的JNIT.class 同一目錄下,而且修改成JNIT.dll或JNIT.so (與java文件中加載的庫名一致)

八、執行java JNIT 運行代碼 .注意不是 java JNIT.class,否則會報沒法加載主類

java JNIT

 

相關文章
相關標籤/搜索