[2014.1.31] Eclipse、MinGW、JNI編寫C++產生dll, Java端調用的完整樣本(附java.

來源:互聯網
上載者:User

問題背景:之前的JNI編程都是基於Android的NDK工具,產生so檔案供android端調用,參見:http://blog.csdn.net/yanzi1225627/article/details/8525720 現在的目標是用eclipse CDT MinGW編寫C++檔案產生PC上可用的動態連結程式庫dll,供純Java調用。本以為很簡單,可沒想到折騰到半夜兩點沒搞定,原因是很多參考文獻資料太老了。好吧,大年初一搞了兩個小時終於拿下。下面是詳細步驟:

準備工作:

將C:\Program Files\Java\jdk1.7.0_45\include路徑下的jni.h和C:\Program Files\Java\jdk1.7.0_45\include\win32路徑下的jni_md.h拷貝到MinGW下的include路徑下,否則會出現找不到#include<jni.h>及不認關鍵字:JNIEXPORT JNICALL JNIEnv的情況。參考文獻中將這兩個路徑直接添加到了C++工程屬性裡的General---Paths and Symbols---GNU C++的include欄,如:


而實際上是沒有必要的,只需按上面的拷貝兩個h檔案到相應位置即可!

1、建立一個java project,包名為org.yanzi.learnjni,主類為LearnJNI,即帶有main函數的類。為了使代碼結構有條理性,再建立一個包:org.yanzi.mylib,建立一個類JNILib.java.代碼如下:

package org.yanzi.mylib;public class JNILib {static{System.loadLibrary("");}public static native void jniPrint(String str);}

我們在這個類裡將本地庫載入進來,由於本地庫還麼有產生,所有System.loadLibrary()函數裡的參數暫時不寫。最關鍵的是下面那句話,聲明了jni裡的函數原型,輸入一個String然後再jni裡列印出來。

2、然後我們在cmd裡利用javah產生與JNILib.java裡jniPrint()函數相對應的JNI的聲明。cmd裡進到所在工程目錄的src檔案夾下:E:\WorkSpaces\Eclipse_Java\LearnJNI\src. 輸入命令:javah org.yanzi.mylib.JNILib

注意:一定要在src檔案夾下輸入javah,只有這樣後面的org.yanzi.mylib.JNILib(包名 + 類名)路徑才能對的上。


重新整理工程,就會看到產生的.h檔案:


3、建立一個C++工程,如:


注意這個C++工程的名字就是未來產生的dll的名字libXXX.dll。這一點跟ndk不同,ndk是通過mk檔案指定動態連結程式庫的名字的。然後點擊next,再建立一個src檔案夾,不是必須的,僅僅是為了讓程式更加規整.然後將剛才產生的org_yanzi_mylib_JNILib.h拷貝到這個src檔案夾下,再建立一個cpp檔案。之後這個.h檔案在java工程就麼有作用了,刪除掉也是可以的,不過為了告訴Java調用的人介面是什麼,這個h檔案就要保留下。為了統一,cpp檔案取名為:org_yanzi_mylib_JNILib.cpp.

原來產生的.h檔案裡沒有形參,加形參後函數體為:

JNIEXPORT void JNICALL Java_org_yanzi_mylib_JNILib_jniPrint
  (JNIEnv *env, jclass jthis, jstring str);

org_yanzi_mylib_JNILib.h檔案的內容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class org_yanzi_mylib_JNILib */#ifndef _Included_org_yanzi_mylib_JNILib#define _Included_org_yanzi_mylib_JNILib#ifdef __cplusplusextern "C" {#endif/* * Class:     org_yanzi_mylib_JNILib * Method:    jniPrint * Signature: (Ljava/lang/String;)V */JNIEXPORT void JNICALL Java_org_yanzi_mylib_JNILib_jniPrint  (JNIEnv *env, jclass jthis, jstring str);#ifdef __cplusplus}#endif#endif
org_yanzi_mylib_JNILib.cpp檔案的內容如下:
/* * org_yanzi_mylib_JNILib.cpp * *  Created on: 2014-2-1 *      Author: Administrator */#include "org_yanzi_mylib_JNILib.h"#include <iostream>using namespace std;JNIEXPORT void JNICALL Java_org_yanzi_mylib_JNILib_jniPrint  (JNIEnv *env, jclass jthis, jstring str){jboolean iscopy = false;const char *charData = env->GetStringUTFChars(str, &iscopy);cout << "Hello, this is from JNI(dll)" <<endl;cout<<"The data from java is:"<<charData << endl;env->ReleaseStringUTFChars(str, charData);}
[ 關鍵一步]選中工程,按alt+enter,在Build----Settings----Tool Settings-----MinGW C++ Linker目錄欄下的Miscellaneous選項下,在linker flags處填入:-Wl,--add-stdcall-alias


然後點擊編譯,在Debug目錄下產生libMyJNILib.dll,libXXX.dll名字可以發現XXX就是我們起的C++的工程名字.


4、產生dll完畢後,C++的就告一段落了。在java工程裡建立一個檔案夾libs,該檔案夾路徑跟src在同一級目錄。將產生的dll拷貝到libs檔案夾。

5、[關鍵一步]在System.loadLibrary()函數裡寫入參數:libMyJNILib,注意而不是MyJNILib,一定是全名,此處和ndk-build產生so不同。JNILib.java代碼如下:

package org.yanzi.mylib;public class JNILib {static{System.loadLibrary("libMyJNILib");}public static native void jniPrint(String str);}

LearnJNI.java代碼如下:

package org.yanzi.learnjni;import org.yanzi.mylib.JNILib;public class LearnJNI {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubJNILib.jniPrint("123456");}}

此刻,點擊run會報錯如下,java.lang.UnsatisfiedLinkError錯誤:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no libMyJNILib in java.library.pathat java.lang.ClassLoader.loadLibrary(ClassLoader.java:1886)at java.lang.Runtime.loadLibrary0(Runtime.java:849)at java.lang.System.loadLibrary(System.java:1088)at org.yanzi.mylib.JNILib.<clinit>(JNILib.java:5)at org.yanzi.learnjni.LearnJNI.main(LearnJNI.java:12)
所以還需要下面重要一步。

6、[關鍵一步]選中工程,依次點擊run---run configurations---LearnJNI,在點擊Arguments,在Vm arguments處填入如下:-Djava.library.path="${workspace_loc}\LearnJNI\libs;${env_var:PATH}"

注意:上面這句話一點都不能錯,其中LearnJNI是java的工程的名字。兩頭的引號不要少,另外裡面是\,因為這是windows下。

經過這些後,點擊run,久違的列印出現了:

另外,在static{
System.loadLibrary("libMyJNILib");
}裡可以加上一句: System.out.println(System.getProperty("java.library.path"));列印path的所有路徑。整體代碼如下:
package org.yanzi.mylib;public class JNILib {static{System.out.println(System.getProperty("java.library.path"));System.loadLibrary("libMyJNILib");}public static native void jniPrint(String str);}

總結,網上常見的誤解之處:1、將mingw裡的bin檔案夾下的mingw32-make.exe改名為make.exe,這一步完全是多餘的!2、在C++的cpp和h檔案裡,將函數的申明(Java關鍵字前面)加個底線_. 如JNIEXPORT void JNICALL _Java_org_yanzi_mylib_JNILib_jniPrint
  (JNIEnv *env, jclass jthis, jstring str) 事實證明這一步是多餘的!3、在-Djava.library.path的配置裡一定要帶“”,有的教程上寫的是: .;./libs,經過列印path印證,這完全就是扯淡!本博文裡的配置才是正確的。4、有的博文說C++的代碼裡才cpp的名字必須和.h名字一樣,這也是扯淡。5、還有的說產生的h檔案原封不動的拷貝到cpp檔案裡,這樣做當然是可以的。但是保留h檔案,在cpp檔案裡include進來也是可以的,推薦這樣做,這會讓程式顯得規整些。
參考:http://jingyan.baidu.com/article/9c69d48f53575d13c9024ec1.html
---------------------本文系原創,轉載請註明作者:yanzi1225627
代碼下載(C++和Java的):http://download.csdn.net/detail/yanzi1225627/6893971


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.