古詩詞大全網 - 藝術簽名 - 如何編寫jni方法

如何編寫jni方法

具體實施步驟如下:

制造

cd盒

。/run.sh

讓我們介紹壹個在這個項目中實現的例子:

1.如何在標準C/c++中調用函數——比如printf(...)

2.如何在C/c++中調用用戶自定義函數

3.jni函數中如何訪問java類中的對象實例域?

4.如何在jni函數中訪問java類中的靜態實例域?

5.jni函數中如何調用java對象的方法?

6.jni函數中如何調用java類的靜態方法?

7.jni函數中如何傳遞基本數據類型參數?

8.jni函數中如何傳遞對象類型參數?

9.jni函數中如何處理字符串?

10.如何在jni函數中處理數組

11.在jni函數中處理返回值。

12.在jni中創建java類對象

二、基本步驟:

在介紹這些例子之前,讓我們看壹下編寫jni方法所需的基本步驟。這些例子都是用c解釋的,至於c++的例子,差別不大,只要稍微修改壹下就可以了。在文件的最後,我們將介紹這些內容:

1,如果要定義jni方法,首先要用java語言聲明這個方法(自然這個聲明過程要在類中進行)。

語句格式如下:

publicnativeoid print();system . loadlibrary(" jnie examples ");}

Jni函數是用關鍵字native method聲明的。

2.編譯該類的源文件,並使用javac命令生成相應的。類文件。

3.使用javah -jni生成java調用和實際C函數之間的轉換存根。存根可以通過從虛擬機堆棧中獲取參數信息並將其傳遞給編譯後的C函數來實現轉換。

4.建立專門的* * *共享庫,從這個* * *共享庫中到處都是這個存根。在上面的示例中,System.loadLibrary用於加載libJNIExamples***共享庫。

第三,配置運行環境:

在編寫簡單的jni函數之前,我們必須配置相應的運行環境。這裏不介紹jdk的配置,這裏主要說壹下庫的路徑。當調用System.loadLibrary(..),編譯器會在我們系統設置的庫路徑中尋找庫。修改路徑的方法和修改任何環境變量的方法基本相同,只需增加壹行LD _ library _ path =。:./lib: $ (LD _ library _ path)在/etc/bash.bashrc目錄下。您也可以從命令行導出。

LD_LIBRARY_PATH=。:./lib:$(LD_LIBRARY_PATH)

四、操作實例分析:

1.例1:在jni中調用標準C中的函數printf();

下面以1為例詳細講解jni方法編寫的詳細過程。

(1),定義包含jni函數的類Print.java:

{

/***********************************************************************

print()函數將調用printf()函數,這是壹個ANSI c

功能*

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */publicnativevoid

print();

system . loadlibrary(" jnie examples ");} }

在上面的例子中,使用了public native void。

print();語句來定義打印類的jni方法。並用sysgem . loadlibrary(“JNI示例”)語句加載libJNIExamples.so庫。註意:加載的語句必須在靜態塊中用static關鍵字聲明,以確保在引用類時庫總是被加載。

(2)編譯這個類:javac Print.java。生成Print.class類,然後用javah生成Print.h: javah Print的頭文件。長生的Print.h文件格式如下:

/*不要編輯此文件-它是機器為類生成的*//*頭文件

Print */jnie export void JNI call Java _ Print _ Print(jnie NV *,

job object);}

粗體字是要實現的JNI功能的生命部分。

(3)編寫JNI函數Print.c的實現部分

JNI export void JNI call Java _ Print _ Print(JNI env * env,job object obj){

printf("example1:在本例中,ANSI C中的printf()函數是

叫\ n”);printf("妳好,輸出是由printf()生成的

ANSI C中的函數\ n ");}

這個文件中實現了壹個簡單的Jni方法。這個方法調用ANSI C中的printf()函數,輸出兩個句子。

(4)將本地函數編譯到libJNIExamples.so的庫中:

用法聲明:gcc-fpic-I/usr/JDK 1.5/include-I/usr/JDK 1.5/include/Linux-shared-o libjniexamples . so print . c

(5)至此,所有的Jni函數都已經實現,可以用java代碼調用La了。

這裏我們使用壹個簡單的類來測試jni方法。以下是PrintTest.java的源代碼:

publistaticvoid main(String[]args){ Print p = new Print();p . print();} }

(6)編譯並執行PrintTest.java,得到如下結果:

示例1:在此示例中,調用了ANSI C中的printf()函數

您好,輸出是由ANSI C中的printf()函數生成的。

由下面描述的每個示例實現的步驟也根據上述步驟來執行。所以只介紹實現的關鍵部分。

2.例2:用C語言調用壹個自定義函數(源程序:java/c函數。javajava/c _ functiontest。javasrc/c函數。證監會/證監會職能。h)。

在java程序中需要調用C實現的函數時,需要在需要調用C函數的類中定義壹個jni方法,在這個jni方法中調用C函數,相當於用java方法封裝C函數,供java程序調用。

在第二個例子中,我們簡單地定義了壹個printHello()函數,它的作用只是輸出壹個句子。如果想在java程序中調用這個函數,只需要在jni函數中調用,和調用ANSI C自帶的prinf()函數沒什麽區別。

3.例3。在jni函數中訪問java類中的對象實例域(源程序:Java/common field . Java/common field test . javasrc/common field . CSRC/common field . h)。

jni函數是用C語言實現的。如果它想訪問java中定義的類對象的實例域,需要三個步驟。

(1)調用GetObjectClass()函數獲取對象的類,函數返回值類型jclass。

(2)調用GetFieldid()函數獲取該類中要訪問的實例域的id。

(3)調用GetXXXField()獲取要訪問的實例域的值。其中XXX對應於要訪問的實例域的類型。

在jni中,java編程語言和C語言數據類型的對應關系是,java原始數據類型前面加‘j’來表示對應的C語言數據類型,比如boolean是jboolean,int是jint,double是jdouble。jobobject類型的對應類型是job object。

在這個例子中,妳可以看到我們已經在java/CommonField.java中定義了CommonField類,它包含int a和int b。

兩個實例域,我們希望在jni函數getCommonField()中訪問和修改它們。妳可以在

這個函數的實現部分可以在src/CommonField.c中找到

以下語句是對域的訪問(以下代碼取自:src/CommonField.c):

jclass class _ Field =(* env)-& gt;GetObjectClass(env,obj);jfieldID fdA =

(* env)-& gt;GetFieldID(env,class_Field," a "," I ");jfieldID fdB =

(* env)-& gt;GetFieldID(env,class_Field,“b”,“I”);jint值A =

(* env)-& gt;GetIntField(env,obj,fdA);jint值B =

(* env)-& gt;GetIntField(env,obj,fdB);

在jni中,所有對jni函數的調用都需要使用env指針,這也是每個局部方法的第壹個參數。它是指向函數指針表的指針,所以必須在每個jni調用之前添加(* env)->GetObjectClass(env,obj)函數調用返回obj對象的類型,其中obj

參數指示所需類型的類對象。

jfieldID GetFieldID(JNIEnv *env,jclass cl,const char name[],const char

Sig[])這個函數返回壹個域名的標識符,Sig表示編碼後的域簽名。所謂編碼簽名,就是編碼類型的簽名。在上面的例子中,類中的A實例字段是int。

類型,用“I”表示,類似地,“b”表示字節,“c”表示字符,“d”表示雙精度,“f”

代表float,“j”代表long,“s”代表short,“v”代表void,“Z”和“Z”代表boolean類型。

GetIntField(env,obj,fdA)用於訪問obj對象的fdA域。如果要訪問的域是double類型,應該使用GetDoubleField(env,obj,fdA)訪問,即類型對應GetXXXField中的XXX。

以下函數用於修改字段的值:

(* env)-& gt;SetIntField(env,obj,fdA,109);(* env)-& gt;SetIntField(env,obj,fdB,145);

這類似於獲取字段的值,只是該函數有壹個附加的值參數要設置給字段。

訪問對象實例域的相關功能如下:

jfieldID GetFieldID(JNIEnv *env,jclass cl,const char name[],const char sig[])

這個函數返回壹個域的標識符。每個參數的含義如下:

環境JNI接口指針;Cl類對象;名稱域名;簽名編碼域簽名

XXX GetXXXField(JNIEnv *env,jobject obj,jfieldID)

該函數返回字段的值。域類型XXX是object、boolean、byte、char、short、int、long、float和double類型之壹。

參數env JNI借口指針;Obj是域所在的對象;Id是域的標識符。

void SetXXXField(JNIEnv *env,jobject obj,jfieldID,XXX value)

該函數用於設置字段的值。XXX和上面的意思壹樣,

參數中env、obj、id的含義同上,值為要設置的值。

4.例4:在jni函數中訪問類的靜態實例域(Java/field . Java Java/field test . javasrc/field . CSRC/field . h)。

因為靜態實例域不屬於壹個對象,它屬於壹個類,所以當妳想訪問靜態實例域時,不像對象的實例域,它調用的函數是(例4舉例說明,下面的代碼取自src/Field.c):

jclass class _ Field =(* env)-& gt;FindClass(env," Field ");jfieldID fdA =

(* env)-& gt;GetStaticFieldID(env,class_Field," a "," I ");jint值A =

(* env)-& gt;GetStaticIntField(env,class_Field,fdA);

(* env)-& gt;SetStaticIntField(env,class_Field,fdA,111);

因為沒有對象,所以必須使用FindClass而不是GetObjectClass來獲取類引用。FindClass()中的第二個參數是類的代碼簽名,它不同於基本類型的代碼簽名。如果類在當前包中,它直接是類的名字。如果類不在當前包中,則應該添加類的詳細路徑:例如,如果字符串類在java.lang包中,則字符串的簽名應該寫成(

ljava/lang/String;),其中(l和;)缺壹不可,其中(;)是表示是的終止符。其他三個功能與訪問對象數據字段基本相同。

5.例5:調用jni函數中java對象的方法(Java/common method . javajava/common method test . javasrc/common mehod . CSRC/common method . h)。

在jni函數中,我們不僅需要訪問java對象的數據字段,有時還需要調用java中類對象已經實現的方法。例5就是關於這種實現的。在src/CommonMethod.c中,我們可以找到以下代碼:

JNI export void JNI call Java _ common method _ call method(JNI env * env,

jobject obj,jint a,jstring s) { printf("例5:在這個

例如,壹個對象的方法將被調用\ n ");jclass

class _ common method =(* env)-& gt;GetObjectClass(env,obj);jmethodID

md =

(* env)-& gt;GetMethodID(env,class_CommonMethod," print ","(il Java/lang/String;)V”);

(* env)-& gt;CallVoidMethod(env,obj,md,a,s);}

這段代碼部分展示了如何實現java類對象函數的調用過程。從上面的代碼部分我們可以看到,實現這個調用有三個步驟,調用了三個函數。

jclass class _ common method =(* env)-& gt;GetObjectClass(env,obj);

jmethodID md =

(* env)-& gt;GetMethodID(env,class_CommonMethod," print ","(il Java/lang/String;)V”);

(* env)-& gt;CallVoidMethod(env,obj,md,a,s);

GetObjectClass(...)函數獲取被調用對象的類;GetMethodID(...)獲取要調用的方法相對於類的ID號;調用XXXMethod(...)調用此方法。

在編寫這個調用過程時,我們仍然需要註意在getmethod(...)功能。在這個例子中,我們需要做的是找到CommonMethod類的print(int)。

壹個String s s)方法,它打印整數a和字符串s。

直男。在函數的編碼簽名部分(粗體加下劃線),getMethodid (env,class _ common method,“print”,”(iljava/lang/string;)V”);

從左到右,可以看到括號中的內容是要調用的方法的參數部分,I表示第壹個參數的類型是int,“Ljava/lang/String;”指示第二個參數的類型為String,v指示返回值類型為空void,如果返回值類型不為空,則使用相應的類型簽名。返回值類型與函數CallxxxMethod(...)來調用這個方法。這個函數的XXX應該被相應的類型替換,在這個例子中是void。如果返回值類型為int類型,則調用此方法的函數為CallIntMethod(...).

6.例6:調用jni函數中java類的靜態方法(Java/method . javajava/method test . javasrc/method . hsrc/method . c)。

例5介紹了如何調用類對象的方法。在這個例子中,我們將介紹如何調用java類的靜態方法。在本例中,我們在/java/Method.java中定義了靜態方法:

公共靜態void print() {

System.out.println("這是類方法的靜態方法");

}

這個函數的作用是打印字符串“這是類方法的靜態方法”;

我們在src/Method.c中實現了調用該方法的jni函數:

JNI export void JNI call Java _ Method _ call Method(JNI env * env,jobject

obj) { printf("例6:在這個例子中,類的靜態

方法將被調用\ n ");jclass class_Method =

(* env)-& gt;FindClass(env," Method ");jmethodID md =

(* env)-& gt;GetStaticMethodID(env,class_Method,“print”,“()V”);

(* env)-& gt;CallStaticVoidMethod(env,class_Method,MD);}

與例5不同,我們要調用的三個函數變成了:

FindClass(...)、GetStaticMethodID(...)、CallStaticVoidMethod(...)。

機理與實施例5中的相同。還是那句話,我就不做過多介紹了。

7.示例7:在jni函數中傳遞基本數據類型參數(Java/basic . javajava/basic test . javasrc/basic . c)

Src/Basic.h)在java/Basic.java中,我們定義了壹個公共的native void raiseValue(int

a)函數,該函數通過將值增加壹個來打印原始值和新值..

這個jni函數的實現部分在src/Basic.c中給出

JNI export void JNI call Java _ Basic _ raise value(JNI env * env,jobject

obj,jint a) { printf("例7:在這個例子中,壹個整數類型

參數將被傳遞給jni方法\ n ");jclass class_Basic =

(* env)-& gt;GetObjectClass(env,obj);jfieldID

FD =(* env)-& gt;GetFieldID(env,class_Basic," value "," I ");jint v =

(* env)-& gt;GetIntField(env,obj,FD);v = v+ a;

(* env)-& gt;SetIntField(env,obj,fd,v);}

在該功能的實現中,GetObjectClass(...),GetFieldID(...)是因為要訪問基類中的值字段。

GetIntField(...)函數得到值,下壹步就是“= v+ a;

它顯示了基本類型參數的處理方式與C語言中基本數據類型的處理方式相同。