一、去年曾經(jīng)做過一個(gè)JAVA和C通信的項(xiàng)目,用C語言實(shí)現(xiàn)某個(gè)特定功能函數(shù),然后在JAVA中調(diào)用。最近又有朋友問起,于是將做過的東西整理成學(xué)習(xí)筆記,希望對(duì)后來者有所幫助,呵呵。
二、jni例子簡(jiǎn)介
jni-java native interface是java與其他語言本地通信的接口,按照jni的約定,java程序可以調(diào)用其他語言編寫的函數(shù),其他語言也可以調(diào)用java實(shí)現(xiàn)的方法,本文介紹了一個(gè)最簡(jiǎn)單的java調(diào)用c方法的例子。主要由兩部分組成,
一個(gè)是java主程序,另一個(gè)是被調(diào)用的dll(由C語言實(shí)現(xiàn))
三、編寫步驟
1、java主程序
//文件testdll.java
public class testdll
{
??????? static
??????? {
?????????????? System.loadLibrary("goodluck");???? //goodluck為調(diào)用的庫名,不必加dll后綴
??????? } ?
??????? /*下面兩個(gè)函數(shù)為C語言實(shí)現(xiàn)的方法*/
??????? public native static int get();
??????? public native static void set(int i);
?????? //主函數(shù)
??????? public static void main(String[] args)
??????? {
??????????????? testdll test = new testdll();
??????????????? test.set(10);???????????????????????????????????????? //調(diào)用了在C中實(shí)現(xiàn)的方法set
??????????????? System.out.println(test.get());????????? //調(diào)用了在C中實(shí)現(xiàn)的方法get
??????????????? System.out.println("hello world!\n");
??????? }
}
2、得到C所需的頭文件
?執(zhí)行如下命令(假設(shè)jdk的安裝路徑為D:\Java\jdk1.5.0\bin)
????D:\Java\jdk1.5.0\bin\javac?? testdll.java???????????????????????????????????????? #生成testdll.class文件
?? D:\Java\jdk1.5.0\bin\javah?? testdll????????????????????????????????????????????????? #根據(jù)testdll.class生成testdll.h文件
?
3、用c語言生成所需的dll
(1)?打開vc6, 建立一個(gè)空的dll項(xiàng)目。
?????? 菜單里選"File"->New->Projects->Win32? dynamic link library,項(xiàng)目名為gookluck(與java文件中的調(diào)用庫名保持一致),第二步里選an empty DLL project.
(2)把前面生成的testdll.h復(fù)制到這個(gè)項(xiàng)目中,再新添下面的testdll.cpp文件
#include "testdll.h"
int i = 0;
JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass)
{
??????? return i;
}
JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint j)
{
???????? i = j;
}
此時(shí)編譯會(huì)報(bào)錯(cuò),因?yàn)檫€缺少幾個(gè)頭文件,因此要把下面兩個(gè)文件復(fù)制到項(xiàng)目目錄中或者vc的頭文件目錄 (即Microsoft Visual Studio\VC98\Include)中,jni.h文件和jni_md.h文件,在我機(jī)器這兩個(gè)文件原始目錄為
D:\Java\jdk1.5.0\include\jni.h
D:\Java\jdk1.5.0\include\win32\jni_md.h
再次編譯,運(yùn)行通過
4、測(cè)試運(yùn)行
??? 將3中得到的testdll.dll文件拷貝到testdll.class所在目錄下,執(zhí)行
????D:\Java\jdk1.5.0\bin\java testdll
??? 運(yùn)行結(jié)果為
??? 10
??? hello world!
?? 說明調(diào)用正確。
五、幾個(gè)注意事項(xiàng)
1、System.loadLibrary("goodluck"); 一句,有時(shí)在別的情況下會(huì)報(bào)錯(cuò),主要原因是程序找不到這個(gè)庫文件所在的路徑,此時(shí)可以檢查path環(huán)境變量是否包含了合適的路徑,或者把dll文件拷貝到系統(tǒng)的dll目錄下,如C:\WINNT\system32下
2、庫文件不必寫后綴名,寫了反而可能錯(cuò),因?yàn)檫@時(shí)程序會(huì)去找testdll.dll.dll文件,自然是不存在了:—)
3、要調(diào)用的方法做本地聲明,關(guān)鍵字為native。并且只需要聲明,而不需要具體實(shí)現(xiàn)。如:
????? public native static void set(int i);
????? public native static int get();
4、函數(shù)返回類型盡量用jni中的基本類型(如整型、字符等),最好不要返回自定義的類,那樣在c里有可能會(huì)引起錯(cuò)誤。
5、在c中寫具體實(shí)現(xiàn)的時(shí)候,我們只關(guān)心兩個(gè)函數(shù)原型
JNIEXPORT jint JNICALL Java_testdll_get (JNIEnv *, jclass);
JNIEXPORT void JNICALL Java_testdll_set (JNIEnv *, jclass, jint);
這里JNIEXPORT都是JNI的關(guān)鍵字,表示此函數(shù)是要被java調(diào)用的,如果是其他語言調(diào)java語言,JNIEXPORT就要變成JNIIMPORT了。
JNICALL告訴C編譯器參數(shù)進(jìn)棧的方式,我們不必關(guān)心.
jint是以JNI為中介使JAVA的int類型與本地c語言的int溝通的一種類型,我們就當(dāng)做int使用。其他類型名可以去查jni幫助文檔.
函數(shù)的名稱是JAVA_再加上java程序的package路徑再加函數(shù)名組成的。
參數(shù)中,我們也只需要關(guān)心在JAVA程序中存在的參數(shù),即第三個(gè)往后的參數(shù)。至于JNIEnv*和jclass我們一般沒有必要去碰它。
知道這些約定后,我們沒有javah程序也可以自己手工寫出testdll.h文件來,不過麻煩些就是了^O^
六、jni的其他參考資料
jni屬于java語言的一部分,其權(quán)威資料自然要到sun的站點(diǎn)上查找(www.sun.com)
以下是我在sun公司網(wǎng)站上找到的一些頁面
http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jniexamp.html
給出了一個(gè)jni的例子,
http://java.sun.com/j2se/1.4.2/docs/guide/jni/index.html
給出了完整的jni介紹,
另外,linux下jni的例子可以參見http://www.linuxmine.com/5629.html
七、附j(luò)avah生成的testdll.h文件。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class testdll */
#ifndef _Included_testdll
#define _Included_testdll
#ifdef __cplusplus
extern "C" {
#endif
/*
?* Class:???? testdll
?* Method:??? get
?* Signature: ()I
?*/
JNIEXPORT jint JNICALL Java_testdll_get
? (JNIEnv *, jclass);
/*
?* Class:???? testdll
?* Method:??? set
?* Signature: (I)V
?*/
JNIEXPORT void JNICALL Java_testdll_set
? (JNIEnv *, jclass, jint);
#ifdef __cplusplus
}
#endif
#endif