閱讀267 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Linux下JNI調用簡單實例操作全過程

開發環境:Linux(Ubuntu 11.04) + JDK 7
實例說明:利用JNI調用本地代碼的方法來實現一個計算Int數組總和的功能


使用JNI調用本地代碼,整個開發流程主要包括以下幾個步驟:
1、創建一個Java類(IntArray.java);
2、使用javac編譯該類(生成IntArray.class);
3、使用javah -jni 產生頭文件(生成IntArray.h);
4、使用本地代碼實現頭文件中定義的方法(編寫IntArray.c);
5、編譯生成本地動態庫(生成libIntArray.so);
6、使用Java運行程序。


一、創建一個Java類(IntArray.java)
class IntArray{
	private native int sumArray(int[] arr);
	public static void main(String[]args){
		IntArray p = new IntArray();
		int arr[] = new int[10];
		for(int i =0;i<10;i++){
			arr[i] = i;
		}

		int sum = p.sumArray(arr);
		System.out.println("Sum = "+sum);
	}

	static{
		System.loadLibrary("IntArray");
		}
}
注:
     1、在Java代碼中聲明本地方法必須有"native"標識符,native修飾的方法,在Java代碼中隻作為聲明存在。例如: private native int sumArray(int[] arr);
     2、在調用本地方法前,必須首先裝載含有該方法的本地庫. 如IntArray.java中所示,置於static塊中,在Java VM初始化一個類時,首先執行這部分代碼,這可保證調用本地方法前,裝載了本地庫。
	static{
		System.loadLibrary("IntArray");
		}


二、使用javac編譯該類(生成IntArray.class)
javac IntArray.java

三、使用javah -jni 產生頭文件(生成IntArray.h)
javah -jni IntArray

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

#ifndef _Included_IntArray
#define _Included_IntArray
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     IntArray
 * Method:    sumArray
 * Signature: ([I)I
 */
JNIEXPORT jint JNICALL Java_IntArray_sumArray
  (JNIEnv *, jobject, jintArray);

#ifdef __cplusplus
}
#endif
#endif


四、使用本地代碼實現頭文件中定義的方法(編寫IntArray.c)
複製IntArray.h成IntArray.c,對於IntArray.c做以下修改:
1、添加頭文件:#include "IntArray.h"
2、去掉以下幾句
#ifndef _Included_IntArray
#define _Included_IntArray
#endif
3、實現頭文件中定義的方法

IntArray.c具體代碼如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class IntArray */
#include "IntArray.h"

#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     IntArray
 * Method:    sumArray
 * Signature: ([I)I
 */
JNIEXPORT jint JNICALL Java_IntArray_sumArray
  (JNIEnv *env, jobject obj, jintArray arr)
{
	jint buf[10] ={0};
	jint i = 0,sum = 0;
	
	(*env)->GetIntArrayRegion(env,arr,0,10,buf);
	
	for(i=0;i<10;i++)
	{
		sum += buf[i];
	}
	
	return sum;
}
#ifdef __cplusplus
}
#endif


五、編譯生成本地動態庫(生成libIntArray.so)
根據本地代碼(IntArray.h,IntArray.c)生成本地動態庫,命令如下:
gcc -I/usr/lib/jvm/java-7-sun/include/ -I/usr/lib/jvm/java-7-sun/include/linux/ -fPIC -shared -o libIntArray.so IntArray.c
注:
其中 -I後麵是java的include文件夾地址,請根據您具體的java版本以及安裝路徑作相應改變;
-f後麵的PIC表示生成的庫中符號是與位置無關的(PIC就是Position Independent Code),關於PIC,可以參考這篇文章
<a href="https://www.gentoo.org/proj/en/hardened/pic-guide.xml">Introduction to Position Independent Code</a>  
-shared表示共享,共享庫後綴名.so可以認為是shared object的簡稱;
-o libIntArray.so,可以理解為編譯後輸出libIntArray.so庫。

六、使用Java運行程序
java IntArray 

可能出現以下結果:
snowdream@snowdream:~$ java IntArray 
Exception in thread "main" java.lang.UnsatisfiedLinkError: no IntArray in java.library.path
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860)
	at java.lang.Runtime.loadLibrary0(Runtime.java:845)
	at java.lang.System.loadLibrary(System.java:1084)
	at IntArray.<clinit>(IntArray.java:15)

分析異常提示可知,我們之前生成的共享庫不在係統默認的共享庫路徑中,程序找不到共享庫報錯。
解決方法有兩個:
1、臨時指定共享庫libIntArray.so的路徑。
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
注:該方法隻在當前會話窗口有效,切換到另外一個終端窗口,則需要重新指定共享庫路徑。

2、運行時加上參數指定共享庫libIntArray.so的路徑。
java -Djava.library.path=.  IntArray
注:-D:設置Java平台的係統屬性。 此時JavaVM可以在當前位置找到該庫。

通過以上任意方法,您都可以得到正確的運行結果:
snowdream@snowdream:~$ java IntArray 
Sum = 45

最後更新:2017-04-02 06:51:52

  上一篇:go Google Code項目代碼托管網站上Git版本控製係統使用簡明教程
  下一篇:go Android Notification與Toast