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


從C/C++到Java,再從Java回到C/C++,今天終於有機會了解了連接Java、C/C++的橋梁——JNI。哈哈!分享一下!
 
一、簡介
JNI是Java native interface的簡寫,可以譯作Java原生接口。Java可以通過JNI調用C/C++的庫,這對於那些對性能要求比較高的Java程序或者Java無法處理的任務無疑是一個很好的方式。
 
二、目的:Java代碼中調用C/C++代碼
三、實現:假設我們的Java程序為J2C.java, C程序為J2C.c, Java與C之間的通信函數名為write2proc;
              那么write2proc的聲明位於J2C.java,實現位於J2C.c;
四、操作

1. 編寫並編譯Java程序

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

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

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++共享庫

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

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

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

8. 執行Java程序,實現跨語言通信
     java J2C

 

五、具體過程

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++。

 

后續的5、6、7、8同前文所述。

 

六、總結

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM