comes from: http://watershitter.iteye.com/blog/477615
1 java 中 c語言函數的聲明
public native static void greeting(); //就像是介面聲明一樣,不過有native!
2 編譯 javac HelloNative.java ,然後使用 javah
javah HelloNative會自動產生c的標頭檔HelloNative.h
3 產生的標頭檔 的 第一句子為
#include <jni.h>
但是gcc裡面預設環境可不知道jni.h是什麼東西,jni.h在jdk的$JAVA_HOME/include或者$JAVA_HOME/include/linux下面,可進去查看一下~
4 接下來就是根據HelloNative.h中聲明的方法寫C語言的實現,注意,自動產生的那個函數名字很長,並且 開頭的 Java是大寫的,大小寫很致命,(最後我的程式在動態庫已經載入好的情況下報錯:java.lang.UnsatisfiedLinkError: HelloNative.greeting()V,就是因為c語言中的函數名字大小寫寫錯,奇怪!編譯不報錯.....)
<<<評論:這個作者有點2,直接複製就好了>>>
5 linux下編譯產生動態庫,注意不同環境的不一樣~
gcc -fPIC -I jdk/include -I jdk/include/linux -shared -o libHelloNative.so HelloNative.c
<<<我當時的編譯命令是:
gcc -fPIC -shared -D_REENTRANT -I $JAVA_HOME/include -I $JAVA_HOME/include/linux Hello.c -o libTestHello.so
我認為作者的命令是出問題的,除非作者真有jdk這個檔案夾而且真在他的父母錄。。。我這條前提也是配置了java環境,不過這不是廢話麼。。>>>
在這裡,我犯的錯:
a,不理解 -I jdk -I 是include,顯示指定庫的庫的地址,自然後面的jdk是要用你的設計地址替換的,
b, 著急的去網上搜尋問題,沒有注意的在linux下,動態連結程式庫的名字 必須是 lib****.so,必須以lib開頭!
<<<我也被b這條搞了很長時間,而且我就只是在這裡出了問題。。真感謝作者>>>
6 編譯產生了 libHelloNative.h之後,接下寫一個test類,如 HelloNativeTest,
當然要調用System.loadLibrary("HelloNative");注意此時不要lib,也不要.so!;
調用執行 HelloNative.greeting();這個時候錯誤又來了:
java.lang.UnsatisfiedLinkError: no HelloNative in java.library.path。這個錯誤很經典,原因:是java找不到庫路徑~:
顯然: libHelloNative.so放在當前路徑 ".",但linux執行的時候卻不知道在當前路徑找。 linux很“傻”很“複雜”~
a. linux下面java.library.path 和環境變臉 jdk/bin的那個個PATH不是一回事情,有另外一個預設變數 LD_LIBRARY_PATH來儲存他的資訊。而windows下,首先java會找目前的目錄,其次,它會去環境變數的地址找!
b。 由於linux的路徑特殊,所以,解決方案 1-可以調用sysout(System.getProperty("java.library.path"));來查看! 然後把 libXXXX.so拷貝到那裡面的目錄下去
2 設定環境變數 export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ,但是設定到哪裡呢? /etc/profile ? or /root/.bashrc 不知道...忘記了linux的載入順序了~
3 可以單次執行時候指定library位置:
java -Djava.library.path=. HelloNativeTest
總結犯錯:
1.不知道gcc編譯時間候指定庫
2.不瞭解java.libray.path的特點,特別是在linux下
3.c語言實現函數的時候拼字錯誤
4.排除問題不夠理性,系統化,出現了煩躁情緒,導致效率低。 時刻明白,機器只是做你指定的事情,總是你自己出錯了~~~
附:gcc 參數解釋(轉載):
最主要的是GCC命令列的一個選項:
-shared 該選項指定產生動態串連庫(讓連接器產生T類型的匯出符號表,有時候也產生弱串連W類型的匯出符號),不用該標誌外部程式無法串連。相當於一個可執行檔
-fPIC:表示編譯為位置獨立的代碼,不用此選項的話編譯後的代碼是位置相關的所以動態載入時是通過代碼拷貝的方式來滿足不同進程的需要,而不能達到真正程式碼片段共用的目的。
-L.:表示要串連的庫在目前的目錄中
-ltest:編譯器尋找動態串連庫時有隱含的命名規則,即在給出的名字前面加上lib,後面加上.so來確定庫的名稱
LD_LIBRARY_PATH:這個環境變數指示動態連接器可以裝載動態庫的路徑。
當然如果有root許可權的話,可以修改/etc/ld.so.conf檔案,然後調用 /sbin/ldconfig來達到同樣的目的,不過如果沒有root許可權,那麼只能採用輸出LD_LIBRARY_PATH的方法了。
4、注意
調用動態庫的時候有幾個問題會經常碰到,有時,明明已經將庫的標頭檔所在目錄 通過 “-I” include進來了,庫所在檔案通過 “-L”參數引導,並指定了“-l”的庫名,但通過ldd命令察看時,就是死活找不到你指定連結的so檔案,這時你要作的就是通過修改LD_LIBRARY_PATH或者/etc/ld.so.conf檔案來指定動態庫的目錄。通常這樣做就可以解決庫無法連結的問題了。
<<<雖然自己當初用過jni(不過貌似是一年前了),如今用起來居然還花了我兩個小時去寫一個helloworld!!可想我當時的基礎多麼不紮實,我卻一直跟別人說我的基礎非常厲害,非常牢固。。真無地自容了>>>