標籤:android style blog http color io os 使用 java
書接上文: 《JNI技術基礎(1)——從零開始編寫JNI代碼》
2.編譯來源程式HelloWorld.java並產生HelloWorld.class
3.產生標頭檔HelloWorld.h
在Linux控制台輸入命令:javah –jni HelloWorld 產生HelloWorld.h標頭檔
//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 __cplusplusextern "C" {#endif/* * Class: HelloWorld * Method: print * Signature: ()V */JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif
這個標頭檔中便告訴了我們需要用C/C++實現的函數的原型,即
JNIEXPORT void JNICALL Java_HelloWorld_print ( JNIEnv * env, jobject obj)
我們只需要按照這種格式完成其函數體的實現即可,函數名的格式:
Java_類名_函數名
參數env代表java虛擬機器環境,Java傳過來的參數和c有很大的不同,需要調用JVM提供的介面來轉換成C類型的,就是通過調用env方法來完成轉換的。
參數obj代表調用的對象,相當於c++的this。當c函數需要改變調用對象成員變數時,可以通過操作這個對象來完成。
4.實現C/C++函數
這塊兒有點偷懶,並沒有逐個字母去敲,而是通過拷貝標頭檔的方法,然後刪除標頭檔中的無用資訊,填充函數體的方法來建立HelloWorld.c檔案,主要是因為JNI函數名都比較複雜,害怕疏忽敲錯某個字元,或者少實現了某個函數,見諒。
//HelloWorld.c#include <jni.h>#include <stdio.h>#include "HelloWorld.h"/* * Class: HelloWorld * Method: print * Signature: ()V */JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *, jobject){ printf("Hello World!\n");}
由上面可以看出,函數體的實現和普通的C/C++函數完全相同,不同的只是函數原型的格式。
需要注意的一點就是記得添加2個標頭檔:jni.h和HelloWorld.h 。
5.編譯產生庫檔案
gcc -I/usr/lib/jvm/java-1.5.0-sun-1.5.0.22/include/linux/
-I/usr/lib/jvm/java-1.5.0-sun-1.5.0.22/include/
-fPIC -shared -o libHelloWorld.so HelloWorld.c
在編譯的時候需要注意的就是記得加上java的兩個路徑,該路徑根據你的java環境的實際安裝路徑而設定,其餘的和編譯普通的動態庫方法相同。
第一個紅色方框中圈出了我們經常範的一個錯誤,就是沒有填寫JNI函數的兩個形參,雖然我們這裡用不到它們,但是也必須寫上,否則無法通過編譯。
//HelloWorld.c#include <jni.h>#include <stdio.h>#include "HelloWorld.h"/* * Class: HelloWorld * Method: print * Signature: ()V */JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *env, jobject obj){ printf("Hello World!\n");}
6.運行Java程式
方框圈出了兩個經常範的錯誤,第一個錯誤產生的原因是找不到剛剛產生的C/C++動態庫,需要手動指定庫的路徑,當然也可以把該庫拷貝到系統庫檔案目錄中。
手動指定動態庫路徑的方法是使用參數 -Djava.library.path
java –Djava.library.path = "." HelloWorld
第二個錯誤產生的原因是使用參數時,"="的前面或者後面使用了空格,去掉等號前後的空格即可。
OK,大功告成,終於看到久違的HelloWorld!
7.附錄:一個簡單的常式
<1>. MyTools.java
//MyTools.javaclass MyTools{ private native int myAdd(int x, int y); private native int mySub(int x, int y); public static void main(String[] args){ int a = 5; int b = 7; int c = new MyTools().myAdd(a, b); int d = new MyTools().mySub(a, b); System.out.println(a + " + " + b + " = " + c); System.out.println(a + " - " + b + " = " + d); } static{ System.loadLibrary("MyTools"); }}
<2>. 自動產生MyTools.h標頭檔
//MyTools.h/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class MyTools */#ifndef _Included_MyTools#define _Included_MyTools#ifdef __cplusplusextern "C" {#endif/* * Class: MyTools * Method: myAdd * Signature: (II)I */JNIEXPORT jint JNICALL Java_MyTools_myAdd (JNIEnv *, jobject, jint, jint);/* * Class: MyTools * Method: mySub * Signature: (II)I */JNIEXPORT jint JNICALL Java_MyTools_mySub (JNIEnv *, jobject, jint, jint);#ifdef __cplusplus}#endif#endif
<3>. 函數體的C/C++實現myTools.c
//MyTools.c#include <jni.h>#include "MyTools.h"/* * Class: MyTools * Method: myAdd * Signature: (II)I */JNIEXPORT jint JNICALL Java_MyTools_myAdd (JNIEnv *env, jobject obj, jint x, jint y){ return (x + y);}/* * Class: MyTools * Method: mySub * Signature: (II)I */JNIEXPORT jint JNICALL Java_MyTools_mySub (JNIEnv *env, jobject obj, jint x, jint y){ return (x - y);}
運行結果:
說明:
1.大多數情況下,JNI都是在Android開發中使用,本文的目的是使用最簡單的語言描述出JNI最基本、最簡單的使用流程,所以並沒有使用Android架構,Android架構中的一大堆東西會阻擋我們的視線,無法專註於對JNI本身的瞭解,而Android下JNI的使用流程和Java是基本相同的,後面會有專門的篇幅介紹Android下JNI編程。
2.此處實現了使用JNI傳遞簡單的參數x和y。
3.通過上面兩個例子,相信大家已經可以寫出自己的簡單的 JNI 應用,但是整個 JNI 系統相當複雜,尤其是參數和傳回值的傳遞方面,後面會有專門篇幅詳細介紹。
< end >
JNI技術基礎(2)——從零開始編寫JNI代碼