linux下jni編程指南之——HelloWorld

該示例源於jni官方編程指南——《The Java™ NativeInterface Programmer’s Guide and Specification》。

    本文通過一個簡單的例子來演示如何使用JNI。我們寫一個JAVA程序,並用它調用一個C函數來打印“Hello World!”。

    這個過程包含下面幾步:

1、 創建一個類(HelloWorld.java)聲明本地方法。

2、 使用javac編譯源文件HollowWorld.java,產生HelloWorld.class。使用javah –jni來生成C頭文件(HelloWorld.h),這個頭文件裏面包含了本地方法的函數原型。

3、 用C/C++代碼寫函數原型的實現。

4、 把C/C++函數實現編譯成一個本地庫,生成libHelloWorld.so。

5、 使用java命令運行HelloWorld程序,類文件HelloWorld.class和本地庫(libHelloWorld.so)在運行時被加載。

     這個流程如下圖所示:

jni-helloworld

  

現在我們按上述步驟一步步的實現:

一、創建HelloWorld.java

class HelloWorld
{
    private native void print();
    public static void main(String[] args)
    {
        new HelloWorld().print();
    }

    static
    {
        System.loadLibrary("HelloWorld");
    }
}

二、生成HelloWorld.class、HelloWorld.h

    1. 編譯HelloWorld.java生成HelloWorld.class

    CD到HelloWorld.java所在的目錄,在命令行中運行如下命令:

javac HelloWorld.java

在當前文件夾編譯生成HelloWorld.class。

    2.生成HelloWorld.h

    在命令行中運行:

javah -jni HelloWorld

可能會提示如下錯誤:

error: cannot access HelloWorld 
file HelloWorld.class not found 
javadoc: error - Class HelloWorld not found. 

錯誤的原因的是java的classpath沒有包含當前路勁,解決辦法有兩種:
用下面的命令行代替
javah -classpath $PWD -jni HelloWorld
或者:

export CLASSPATH=$CLASSPATH:$PWD; javah -jni HelloWorld

這樣就在能在當前目錄下生成了HelloWorld.h,內容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */

#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     HelloWorld
 * Method:    print
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_HelloWorld_print
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

該文件中包含了一個函數Java_HelloWorld_print的聲明。這裏麪包含兩個參數,非常重要,後面講實現的時候會講到。

三、用C/C++代碼寫函數原型的實現

    在當前目錄下創建HelloWorld.cpp, 內容如下:

#include <jni.h>
#include <stdio.h>
#include "HelloWorld.h"

JNIEXPORT void JNICALL 

    Java_HelloWorld_print(JNIEnv *env, jobject obj)
{
    printf("Hello World!\n");
}
     注意必須要包含jni.h頭文件,該文件中定義了JNI用到的各種類型,宏定義等。jni頭文件存在於你jdk的安裝路勁下,比如我的jdk安裝在 /usr/lib/jvm/java-1.5.0-sun 目錄下, 那麼jni.h就存在於/usr/lib/jvm/java-1.5.0-sun/include目錄下,這個路徑待會會用到。

    另外需要注意Java_HelloWorld_print的兩個參數,本例比較簡單,不需要用到這兩個參數。但是這兩個參數在JNI中非常重要。env代表java虛擬機環境,Java傳過來的參數和c有很大的不同,需要調用JVM提供的接口來轉換成C/C++類型的,就是通過調用env方法來完成轉換的。obj代表調用的對象,相當於c++的this。當 c/C++ 函數需要改變調用對象成員變量時,可以通過操作這個對象來完成。

四、 把C/C++函數實現編譯成一個本地庫,生成libHelloWorld.so

    在終端執行如下命令生成libHelloWorld.so:

g++ -I/usr/lib/jvm/java-1.5.0-sun/include/linux/ -I/usr/lib/jvm/java-1.5.0-sun/include/ -fPIC -shared -o libHelloWorld.so HelloWorld.cpp

    在當前目錄生成libHelloWorld.so。注意一定需要包含Java的include目錄(請根據自己系統環境設定),因爲Helloworld.c中包含了jni.h。

另外一個值得注意的是在HelloWorld.java中我們LoadLibrary方法加載的是“HelloWorld”,可我們生成的Library卻是libHelloWorld。這是Linux的鏈接規定的,一個庫的必須要是:lib+庫名+.so。鏈接的時候只需要提供庫名就可以了

五、 使用java命令運行HelloWorld程序

    在終端中輸入運行HelloWorld程序:

java HelloWorld
能會出現如下錯誤:

Exception in thread "main" java.lang.NoClassDefFoundError: HelloWorld

出現這個錯誤也是因爲CLASSPATH環境變量沒有包含當前目錄,解決方法與上面提到的一樣:

java -classpath $PWD HelloWorld

或者:

export CLASSPATH=$CLASSPATH:$PWD; java HelloWorld

緊接着可能也會出現下面的一個錯誤:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no HelloWorld in java.library.path 
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1682) 
    at java.lang.Runtime.loadLibrary0(Runtime.java:822) 
    at java.lang.System.loadLibrary(System.java:993) 
    at HelloWorld.<clinit>(HelloWorld.java:11)

這個錯誤的原因是LD_LIBRARY_PATH環境變量沒有包含當前目錄,HelloWorld程序無法找到libHelloWorld.so這個庫,解決辦法如下:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD; export CLASSPATH=$CLASSPATH:$PWD; java HelloWorld

這樣就能看到我們想要的結果了:

Hello World! 

其實,在生成HelloWorld.h之前,我們就可以先修改好 LD_LIBRARY_PATH、CLASSPATH 這兩個環境變量:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD; export CLASSPATH=$CLASSPATH:$PWD

這樣生成HelloWorld.h 就只需命令: javah –jni HelloWorld; 運行HelloWorld只需命令: java HelloWorld 了。

在這裏給出一個jni學習資料的下載鏈接:

http://download.csdn.net/source/3277862

發佈了11 篇原創文章 · 獲贊 3 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章