JNI——Java調用C/C++函數

從C/C++到Java,再從Java回到C/C++,今天終於有機會了解了鏈接Java、C/C++的橋樑——JNI。哈哈!分享一下!
 
1、簡介
JNI是Java native interface的簡寫,能夠譯做Java原生接口。Java能夠經過JNI調用C/C++的庫,這對於那些對性能要求比較高的Java程序或者Java沒法處理的任務無疑是一個很好的方式。
 
2、目的:Java代碼中調用C/C++代碼
3、實現:假設咱們的Java程序爲J2C.java, C程序爲J2C.c, Java與C之間的通訊函數名爲write2proc;
              那麼write2proc的聲明位於J2C.java,實現位於J2C.c;
4、操做

1. 編寫並編譯Java程序html

    javac J2C.java => J2C.class
2. 生成C/C++頭文件java

    javah J2C => J2C.h (安裝JDK後,$JAVA_HOME應該已加入$PATH, 不然使用絕對路徑,例如/usr/bin/javah)linux

3. 編寫對應的C/C++程序:J2C.c數組

4. 生成C/C++目標文件
    gcc -I/usr/lib/jvm/java-6-openjdk-amd64/include -I/usr/lib/jvm/java-6-openjdk-amd64/include/linux -fPIC -c J2C.c => J2C.o
5. 生成C/C++共享庫app

    gcc -shared -Wl,-soname,libj2c.so.1 -o libj2c.so.1.0 J2C.o => libj2c.so.1.0jvm

6. 重命名cp libj2c.so.1.0 libj2c.so => libj2c.so函數

7. 將共享庫加入動態連接庫的路徑(此例爲當前目錄)
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.性能

8. 執行Java程序,實現跨語言通訊
     java J2Cthis

 

5、具體過程編碼

1. 編寫並編譯J2C.java

 1 import java.lang.management.ManagementFactory;
 2 import java.lang.management.RuntimeMXBean;
 3 
 4 public class J2C
 5 { 
 6      static 
 7      { 
 8           try{ 
 9                // 此處即爲本地方法所在連接庫名
10                System.loadLibrary("j2c");
11           } catch(UnsatisfiedLinkError e) 
12           { 
13                System.err.println( "Cannot load J2C library:\n " + 
14                e.toString() ); 
15           } 
16      }
17 
18      //聲明的本地方法
19      public static native int write2proc(int pid);
20 
21      public static void main(String[] args){
22 
23           //獲取本進程(即主線程)的pid
24           final RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
25           final String info = runtime.getName();
26           final int index = info.indexOf("@");
27 
28 
29           if (index != -1) {
30                final int pid = Integer.parseInt(info.substring(0, index));
31                System.out.println(info);
32                System.out.println(pid);
33 
34                write2proc(pid);
35           }
36 
37 
38           try{
39                Thread.sleep(8000);
40           } catch(InterruptedException e){
41                e.printStackTrace();
42           }
43      }
44 }

 

note:Java程序中System.loadLibrary參數名錶示要載入的C/C++共享庫,第6步生成的共享庫名必須與該參數一致

           即System.loadLibrary(Name) 對應共享庫名libName.so (共享庫名必須以lib開頭)

2. 生成C頭文件J2C.h:javah J2C

 1 /* DO NOT EDIT THIS FILE - it is machine generated */
 2 #include <jni.h>
 3 /* Header for class J2C */
 4 
 5 #ifndef _Included_J2C
 6 #define _Included_J2C
 7 #ifdef __cplusplus
 8 extern "C" {
 9 #endif
10 /*
11  * Class: J2C
12  * Method: write2proc
13  * Signature: (I)I
14  */
15 JNIEXPORT jint JNICALL Java_J2C_write2proc
16      (JNIEnv *, jclass, jint);
17 
18 #ifdef __cplusplus
19 }
20 #endif
21 #endif

 

 

note:1. 頭文件自動生成,不要修改它;

          2. 函數JNIEXPORT jint JNICALL Java_J2C_write2proc(JNIEnv *, jclass, jint);

              按照註釋的說明是在J2C.java文件的類J2C的方法write2proc處定義,故C程序的實現函數必須與該處簽名一致;

 

3. 編寫C程序J2C.c

 1 #include <stdio.h>
 2 
 3 #include "J2C.h"
 4 
 5 JNIEXPORT int JNICALL Java_J2C_write2proc(JNIEnv * env, jobject arg, jint pid) 
 6 {
 7 
 8      printf("current pid is %d\n", pid);
 9 
10      return 0;
11 
12 }

 

4. 編譯C程序

    由於C程序裏#include "J2C.h"而J2C.h又#include <jni.h>, 而gcc裏面默認環境並不知道jni.h是什麼東西,故編譯時須要告訴編譯器jni.h的位置( jni.h在jdk 的$JAVA_HOME/include下面),因此纔有了上面的編譯參數;

  由於使用gcc編譯獲得動態庫,在jni調用的時候,某些狀況會有異常, 可嘗試改用g++。

 

後續的五、六、七、8同前文所述。

 

6、總結

1. Java中方法的原型聲明與C/C++對應的實現文件定義必須一致(能夠經過自動生成的C/C++頭文件來比較),尤爲是類名和方法名;

2. Java中System.loadLibrary()載入的共享庫名必須與後面C/C++生成的共享庫名一致;

3. JNI步驟很清晰,可是不少小細節容易引發各類問題,so細心點。

 

附上C/C++中Java方法簽名及常見的錯誤:

1. 在C/C++函數中實現Java中定義的方法

函數聲明, Java_完整類名_方法名, 完整類名包括了包名.

在註釋中咱們能夠看到這樣一個東西 Signature, 這個是方法的簽名. 關於Signature, 下面經過一個表格來講明.

java類型 Signature 備註
boolean Z  
byte B  
char C  
short S  
int I  
long L  
float F  
double D  
void V  
object L用/分割的完整類名 例如: Ljava/lang/String表示String類型
Array [簽名 例如: [I表示int數組, [Ljava/lang/String表示String數組
Method (參數簽名)返回類型簽名 例如: ([I)I表示參數類型爲int數組, 返回int類型的方法

如本例中的函數聲明:

JNIEXPORT jint JNICALL Java_J2C_write2proc(JNIEnv *, jobject, jint);

註釋中的簽名是 Signature: (I)I

 

2. JNI開發中碰到的"error: request for member 'GetStringUTFChars' in something not a structure or union"

這個錯誤在C、C++中須要分開處理。

C代碼: (*env)->GetStringUTFChars(env, string, 0);

C++代碼: env->GetStringUTFChars(string, 0);

緣由:C語言中使用的是結構體的函數指針, 而在C++中使用的仍是struct, 咱們知道struct在C++中和class的功能是幾乎同樣的, struct也能夠用來定義類, 因此env在C++中是個類對象的指針.

下面以IBM的一篇經典文章爲例 http://www.ibm.com/developerworks/cn/java/l-linux-jni/

C++:

// 從 instring 字符串取得指向字符串 UTF 編碼的指針

const char *str = env->GetStringUTFChars( instring, JNI_FALSE ); 
printf("Hello,%s\n",str); 
// 通知虛擬機本地代碼再也不須要經過 str 訪問 Java 字符串。
env->ReleaseStringUTFChars( instring, str );

C:

// 從 instring 字符串取得指向字符串 UTF 編碼的指針

const char *str = (*env)->GetStringUTFChars( env, instring, JNI_FALSE ); 
printf("Hello,%s\n",str); 
// 通知虛擬機本地代碼再也不須要經過 str 訪問 Java 字符串。
(*env)->ReleaseStringUTFChars( env, instring, str );

 

3. 當使用 JNI 從 Java 程序訪問本機代碼時,您會遇到許多問題。您會遇到的三個最多見的錯誤是:

沒法找到動態連接:Exception in thread "main" java.lang.UnsatisfiedLinkError

它所產生的錯誤消息是:java.lang.UnsatisfiedLinkError。這一般指沒法找到共享庫,或者沒法找到共享庫內特定的本機方法。


沒法找到共享庫文件:Cannot load library

當用 System.loadLibrary(String libname) 方法(參數是文件名)裝入庫文件時,請確保文件名拼寫正確以及沒有指定擴展名。還有,確保庫文件的位置在類路徑中,從而確保 JVM 能夠訪問該庫文件。


沒法找到具備指定說明的方法

確保您的 C/C++ 函數實現擁有與頭文件中的函數說明相同的說明。

 

How to resolve java.lang.UnsatisfiedLinkError 
User should check whether-

1. System.loadLibrary is passed an incorrect parameter:

     - Windows: To load Name.dll, Name is passed to the loadLibrary method.

     -AIX, HP-UX, Solaris, Linux: To load libName.so or libName.a, libName is passed to the loadLibrary method
2. Native library is already loaded-

     If the native library was already loaded by an application and the same application tries to load it again, this can cause this error.
3. Native Library is not present in java.library.path or LD_LIBRARY_PATH

 

參考:

http://www.ibm.com/developerworks/cn/java/l-linux-jni/

http://www.cnblogs.com/icejoywoo/archive/2012/02/22/2363709.html

http://www.cnblogs.com/mimi1/archive/2012/09/27.html

http://stackoverflow.com/questions/11624503/i-am-trying-to-use-the-freeimage-library-in-my-project-causes-error

相關文章
相關標籤/搜索