2011年4月11日 星期一

Java Native Interface (JNI)入門 -- 觀念篇

眾所皆知,Java是一種可以跨平台的程式語言,而JNI是可以讓Java applications跟用其它語言(諸如C,C++)寫成的應用程式或程式庫互相呼叫使用。
JNI技術的出現主要基於三個方面的應用需求:
A.解決效能問題。
B.解決周邊硬體介面呼叫問題或解決標準JAVA預設函式庫中支援你的應用程式所需的平台相關的功能。。
C.可沿用已開發過的C語言,不須重複開發移植到Java系統。

由於有C/C++的成分存在,因此JNI就失去了跨平台使用,例如從Microsoft要移植到Linux的話,C/C++的部分就必須重新修改編譯過。
以下整理了JAVA呼叫C、C呼叫JAVA兩種HelloWorld教學,大致上應該可以了解一些JNI的觀念。

實作環境:
Ubuntu 10.10
Java 1.6.0_23
gcc 4.4.5
(本次實作在PC上執行)

實作一、JAVA呼叫C
下圖是JNI中JAVA呼叫C的編譯過程:

整個過程中第5步驟如果在Linux下產生的是 .so檔案,如果是windows環境下則可以用Microsoft Visual C++產生出 .dll檔案同樣也可以使用。

Step 1: Write the Java Code
建立HelloWorld.java


Step 2: Compile the Java Code
執行javac HelloWorld.java //編譯

Step 3: Create the .h File (這是自動產生出的不要去編輯)
javah -jni HelloWorld //自動產生對應C標頭檔,內容中粗體為C的函式宣告:


Step 4: Write the Native Method Implementation
建立jni.c


Step 5: Create a Shared Library
執行
gcc -fPIC -shared -I<jni.h 目錄> -I<jni_md.h目錄> jni.c -o libjni.so
在實作中我下的指令如下:
gcc -fPIC -shared -I/usr/local/jdk1.6.0_23/include -I/usr/local/jdk1.6.0_23/include/linux jni.c -o libjni.so

Step 6: Run the Program
執行:
LD_LIBRARY_PATH=`pwd` java HelloWorld

完成。

實作二、C呼叫JAVA
Step 1: Write the Java Code
建立HelloWorld.java


Step 2: Compile the Java Code
執行javac HelloWorld.java //編譯

Step 3: Write the C Code
建立 Invoke.c


Step 4: Compile the C Code
執行
gcc -I<jni.h 目錄> -I<jni_md.h目錄> -L<libjvm.so目錄> -ljvm Invoke.c -o Invoke
在實作中我下的指令如下:
gcc -I/usr/local/jdk1.6.0_23/include -I/usr/local/jdk1.6.0_23/include/linux -L/usr/local/jdk1.6.0_23/jre/lib/amd64/server -ljvm Invoke.c -o Invoke

Step 5: Run the C Program

export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/jdk1.6.0_23/jre/lib/amd64/server"
./Invoke

完成。

註:如果執行中見下列錯誤訊息則代表沒有執行步驟5 export ...... (冒號後接的是 libjvm.so 的目錄)
./Invoke: error while loading shared libraries: libjvm.so: cannot open shared object file: No such file or directory

本文內容並非在Android平台執行,主要是寫出我學習Native開發的筆記分享,對於本身不熟JNI的再加上Android部分我覺得多少會提高入門的門檻,因此時間的允許情況下我計畫寫成觀念篇、實戰篇及Android實戰篇,由於本身對於JNI觀念也處於學習中,文章的部分有時間就慢慢來完成了。

參考:
JNI Wiki
http://en.wikipedia.org/wiki/Java_Native_Interface
Lesson: Compiling and Running a Java Program with a Native Method
http://www.cs.rtu.lv/PharePub/Java/Tutorial/native1.1/stepbystep/Default.htm
Trail: Java Native Interface
http://www.cs.rtu.lv/PharePub/Java/Tutorial/native1.1/Default.htm


===========延伸閱讀========================================
第一支Android NDK程式--HelloJni

Java Native Interface (JNI) 的使用時機及影響

Java Native Interface (JNI)入門 -- 觀念篇

Java Native Interface (JNI) 實戰篇

Java Native Interface (JNI) Android實戰篇(使用NDK) -- HelloUart

Java Native Interface (JNI) Android算數篇(使用NDK) -- Fibonacci Sequence

Java Native Interface (JNI) Android C++語言篇--以Hello-JNI為例

Java Native Interface (JNI) Android C++語言篇--以Hello-JNI為例

Java Native Interface (JNI) Android C程式間傳遞篇--以two-libs為例

Java Native Interface (JNI) Android C呼叫Java (底層呼叫上層)間傳遞篇--以靜態或動態類型為例

Android NDK Beginner’s Guide


===========相關閱讀========================================
在 Ubuntu 12.04 LTS 安裝 Android SDK&NDK 開發環境

7 則留言:

  1. JAVA呼叫C的實作中
    我想請問在STEP3
    Create the .h File
    文章中提到會自動產生.h檔
    但我執行javah -jni Location後
    卻只產生下列訊息
    請問我該如何解決 謝謝

    Exception in thread "main" java.io.IOException: can't find class file Location.class in java.net.URLClassLoader{urls=[file:/usr/lib/jvm/java-1.5.0-gcj-4.5/jre/lib/rt.jar], parent=gnu.gcj.runtime.SystemClassLoader{urls=[file:./], parent=gnu.gcj.runtime.ExtensionClassLoader{urls=[], parent=null}}}
    at gnu.classpath.tools.javah.Main.getClass(libgcj-tools.so.11)
    at gnu.classpath.tools.javah.Main.run(libgcj-tools.so.11)
    at gnu.classpath.tools.javah.Main.main(libgcj-tools.so.11)

    回覆刪除
  2. 從訊息中:can't find class file Location.class
    我的解讀是應該是沒找到Location.class檔案,您在確認看看^^

    回覆刪除
  3. 我執行完javac的指令後
    在.java的資料夾中有出現.class的檔案

    回覆刪除
  4. 蚊子你好 我想請問一下,
    jni 有對應Camera的簽名嗎@@?我不知道要用哪個,順便再請問一下,多個簽名要如何寫?

    例如public void f(byte[] byte,Camera camera)


    回覆刪除
  5. 您好,我執行Java Call C第四個步驟,出現錯誤訊息如下:
    jni.c:1: fatal error: jni.h: No such file or directory
    compilation terminated
    請問會是哪裡出錯?謝謝。

    回覆刪除
    回覆
    1. 按照上面寫的路徑,是在usr/local/底下,但是我的路徑是在/usr/lib底下均多,所以我改了路徑在去執行第四個步驟就成功了.

      刪除
  6. 請問,LD_LIBRARY_PATH=`pwd` java HelloWorld的pwd是固定的嗎?
    因為我執行後會出現很多錯誤訊息:java.lang.noclassdeffounderror

    回覆刪除