☆
JNI
【摘】Java 本機(jī)接口(Java Native Interface (JNI))是一個本機(jī)編程接口,它是 Java 軟件開發(fā)工具箱(Java Software Development Kit (SDK))的一部分。JNI 允許 Java 代碼使用以其它語言(譬如 C 和 C++)編寫的代碼和代碼庫。Invocation API(JNI 的一部分)可以用來將 Java 虛擬機(jī)(JVM)嵌入到本機(jī)應(yīng)用程序中,從而允許程序員從本機(jī)代碼內(nèi)部調(diào)用 Java 代碼。
☆
JNI [Java調(diào)用其他語言]運(yùn)用方向【摘】
1)希望用更低級、更快的編程語言去實(shí)現(xiàn)對時間有嚴(yán)格要求的代碼。
2)希望從 Java 程序訪問舊代碼或代碼庫。
3)需要標(biāo)準(zhǔn) Java 類庫中不支持的依賴于平臺的特性。
☆
步驟【摘】
1)編寫 Java 代碼。從編寫 Java 類開始,這些類執(zhí)行三個任務(wù):聲明將要調(diào)用的本機(jī)方法;裝入包含本機(jī)代碼的共享庫;然后調(diào)用該本機(jī)方法。
2)編譯 Java 代碼。在使用 Java 類之前,必須成功地將它們編譯成字節(jié)碼。
3)創(chuàng)建 C/C++ 頭文件。C/C++ 頭文件將聲明想要調(diào)用的本機(jī)函數(shù)說明。然后,這個頭文件與 C/C++ 函數(shù)實(shí)現(xiàn)(請參閱步驟 4)一起來創(chuàng)建共享庫(請參閱步驟 5)。
4)編寫 C/C++ 代碼。這一步實(shí)現(xiàn) C 或 C++ 源代碼文件中的函數(shù)。C/C++ 源文件必須包含步驟 3 中創(chuàng)建的頭文件。
5)創(chuàng)建共享庫文件。從步驟 4 中創(chuàng)建的 C 源代碼文件來創(chuàng)建共享庫文件。
6)運(yùn)行 Java 程序。運(yùn)行該代碼,并查看它是否有用。我們還將討論一些用于解決常見錯誤的技巧。
☆
第一步:編寫Java代碼

JavaCallC.java
package jnidemo;
public class JavaCallC {
/**
* native 關(guān)鍵字告訴 Java 編譯器:方法是用 Java 類之外的本機(jī)代碼實(shí)現(xiàn)
* 只能在 Java 類中聲明本機(jī)方法,而不能實(shí)現(xiàn)它
*/
public native void voidMethod();
public native int intMethod(int arg);
public native boolean boolMethod(boolean arg);
public native int intArrayMethod(int [] args);
public native String [] strArrayMethod(String [] args);
public native String [] getName();
public static void main(String[] args) {
/**
* 裝入了包含本機(jī)方法的實(shí)現(xiàn)的共享庫文件
* (基于 UNIX 的平臺上的共享庫文件通常含有前綴"lib")
*/
System.loadLibrary("libJavaCallC");
JavaCallC javaCallC=new JavaCallC();
javaCallC.voidMethod();
System.out.println("intMethod="+javaCallC.intMethod(2008));
System.out.println("boolMethod="+javaCallC.boolMethod(true));
System.out.println("intArrayMethod="+javaCallC.intArrayMethod(new int[]{1,2,3}));
String [] result=javaCallC.strArrayMethod(new String []{"happy","new","year"});
for(String str:result){
System.out.print(str);
}
result=javaCallC.getName();
for(String str:result){
System.out.print(str);
}
}
}
☆
第二步:編譯Java代碼
javac JavaCallC.java
☆
第三步:創(chuàng)建C頭文件
javah jnidemo.JavaCallC
得到頭文件如下:

jnidemo_JavaCallC.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class jnidemo_JavaCallC */
#ifndef _Included_jnidemo_JavaCallC
#define _Included_jnidemo_JavaCallC
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: jnidemo_JavaCallC
* Method: voidMethod
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_jnidemo_JavaCallC_voidMethod
(JNIEnv *, jobject);
/*
* Class: jnidemo_JavaCallC
* Method: intMethod
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_jnidemo_JavaCallC_intMethod
(JNIEnv *, jobject, jint);
/*
* Class: jnidemo_JavaCallC
* Method: boolMethod
* Signature: (Z)Z
*/
JNIEXPORT jboolean JNICALL Java_jnidemo_JavaCallC_boolMethod
(JNIEnv *, jobject, jboolean);
/*
* Class: jnidemo_JavaCallC
* Method: intArrayMethod
* Signature: ([I)I
*/
JNIEXPORT jint JNICALL Java_jnidemo_JavaCallC_intArrayMethod
(JNIEnv *, jobject, jintArray);
/*
* Class: jnidemo_JavaCallC
* Method: strArrayMethod
* Signature: ([Ljava/lang/String;)[Ljava/lang/String;
*/
JNIEXPORT jobjectArray JNICALL Java_jnidemo_JavaCallC_strArrayMethod
(JNIEnv *, jobject, jobjectArray);
/*
* Class: jnidemo_JavaCallC
* Method: getName
* Signature: ()[Ljava/lang/String;
*/
JNIEXPORT jobjectArray JNICALL Java_jnidemo_JavaCallC_getName
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
除了 Java 聲明中的一般參數(shù)以外,所有這些函數(shù)的參數(shù)表中都有一個指向 JNIEnv 和 jobject 的指針。指向 JNIEnv 的指針實(shí)際上是一個指向函數(shù)指針表的指針,這些函數(shù)提供各種用來在 C 和 C++ 中操作 Java 數(shù)據(jù)的能力。
jobject 參數(shù)引用當(dāng)前對象。因此,如果 C 或 C++ 代碼需要引用 Java 函數(shù),則這個 jobject 充當(dāng)引用或指針,返回調(diào)用的 Java 對象。
☆
第四,五步:編寫C代碼[Windows下得到DLL]
1)開啟VC;
2)新建->Win32 Dynamic-Link Library工程,工程名為libJavaCallC;
3)在Source Files下新建C文件javaCallC.c;
4)在HeaderFiles下引入頭文件jnidemo_JavaCallC.h;
5)配置路徑,在Tools->Options->Directories下加入JAVA的頭文件,例如:
C:\Program Files\Java\jdk1.6.0\include
C:\Program Files\Java\jdk1.6.0\include\win32
不然編譯會報(bào)錯[Cannot open include file: 'jni.h']

javaCallC.c
#include <jni.h>
#include "jnidemo_JavaCallC.h"
#include <iostream.h>
#include <string.h>
#ifndef NULL
#define NULL 0
#endif
/*
* Class: jnidemo_JavaCallC
* Method: voidMethod
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_jnidemo_JavaCallC_voidMethod
(JNIEnv * env, jobject obj){
printf("voidMethod called!\n");
}
/*
* Class: jnidemo_JavaCallC
* Method: intMethod
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_jnidemo_JavaCallC_intMethod
(JNIEnv * env, jobject obj, jint num){
return num+num;
}
/*
* Class: jnidemo_JavaCallC
* Method: boolMethod
* Signature: (Z)Z
*/
JNIEXPORT jboolean JNICALL Java_jnidemo_JavaCallC_boolMethod
(JNIEnv * env, jobject obj, jboolean b){
return !b;
}
/*
* Class: jnidemo_JavaCallC
* Method: intArrayMethod
* Signature: ([I)I
*/
JNIEXPORT jint JNICALL Java_jnidemo_JavaCallC_intArrayMethod
(JNIEnv * env, jobject obj, jintArray jarray){
int i,sum=0;
jsize len=(*env)->GetArrayLength(env,jarray);
//獲取數(shù)組指針
jint * intArray=(*env)->GetIntArrayElements(env,jarray,0);
for(i=0;i<len;i++)
sum+=intArray[i];
//釋放空間
(*env)->ReleaseIntArrayElements(env,jarray,intArray,0);
return sum;
}
/*
* Class: jnidemo_JavaCallC
* Method: strArrayMethod
* Signature: ([Ljava/lang/String;)[Ljava/lang/String;
*/
JNIEXPORT jobjectArray JNICALL Java_jnidemo_JavaCallC_strArrayMethod
(JNIEnv * env, jobject obj, jobjectArray jobjArray){
int i,strLen;
jsize len=(*env)->GetArrayLength(env,jobjArray);
for(i=0;i<len;i++){
jstring element=(*env)->GetObjectArrayElement(env,jobjArray,i);
const char *str = (*env)->GetStringUTFChars(env, element, 0);
char buff[128];
strcpy(buff, str);
strLen=strlen(buff);
buff[strLen]=' ';
buff[++strLen]='\0';
(*env)->SetObjectArrayElement(env,jobjArray,i,(*env)->NewStringUTF(env,buff));
}
return jobjArray;
}
/*
* Class: jnidemo_JavaCallC
* Method: getName
* Signature: ()[Ljava/lang/String;
*/
JNIEXPORT jobjectArray JNICALL Java_jnidemo_JavaCallC_getName
(JNIEnv * env, jobject obj){
int i=0;
jobjectArray result = NULL;
jsize len = 4;
jclass strClass = (*env)->FindClass(env, "java/lang/String");
//新建數(shù)組
result = (*env)->NewObjectArray(env, len, strClass, NULL);
(*env)->SetObjectArrayElement(env,result,i++,(*env)->NewStringUTF(env,"wo"));
(*env)->SetObjectArrayElement(env,result,i++,(*env)->NewStringUTF(env,"xing"));
(*env)->SetObjectArrayElement(env,result,i++,(*env)->NewStringUTF(env,"wo"));
(*env)->SetObjectArrayElement(env,result,i++,(*env)->NewStringUTF(env,"su"));
return result;
}
void main(){};
API和類型對照請參考后面的參考資料。
附:C 和 C++ 函數(shù)實(shí)現(xiàn)的比較
C 和 C++ 代碼幾乎相同;唯一的差異在于用來訪問 JNI 函數(shù)的方法。在 C 中,JNI 函數(shù)調(diào)用由“(*env)->”作前綴,目的是為了取出函數(shù)指針?biāo)玫闹怠T?nbsp;C++ 中,JNIEnv 類擁有處理函數(shù)指針查找的內(nèi)聯(lián)成員函數(shù)。下面將說明這個細(xì)微的差異,其中,這兩行代碼訪問同一函數(shù),但每種語言都有各自的語法。
C 語法: jsize len = (*env)->GetArrayLength(env,array);
C++ 語法: jsize len =env->GetArrayLength(array);

Linux得到SO
gcc -I /usr/local/jdk1.5.0_01/include -I /usr/local/jdk1.5.0_01/include/linux -c javaCallC.c
gcc -shared -o libJavaCallC.so javaCallC.o
【不知道哪里出問題,執(zhí)行的時候一直報(bào)錯no libJavaCallC in java.library.path】
☆
第六步:運(yùn)行JAVA程序
1)將libJavaCallC.dll放在環(huán)境變量的路徑中;
2)運(yùn)行:java jnidemo.JavaCallC

運(yùn)行結(jié)果
voidMethod called!
intMethod=4016
boolMethod=false
intArrayMethod=6
happy new year woxingwosu
☆
JNI[其他語言調(diào)用JAVA]
1)希望實(shí)現(xiàn)的這部分代碼是平臺無關(guān)的,它將用于跨多種平臺使用的功能。
2)需要在本機(jī)應(yīng)用程序中訪問用 Java 語言編寫的代碼或代碼庫。
3)希望從本機(jī)代碼利用標(biāo)準(zhǔn) Java 類庫
☆
開發(fā)步驟【摘】
1)編寫 Java 代碼。這個步驟包含編寫一個或多個 Java 類,這些類實(shí)現(xiàn)(或調(diào)用其它方法實(shí)現(xiàn))您想要訪問的功能。
2)編譯 Java 代碼。在能夠使用這些 Java 類之前,必須成功地將它們編譯成字節(jié)碼。
3)編寫 C/C++ 代碼。這個代碼將創(chuàng)建和實(shí)例化 JVM,并調(diào)用正確的 Java 方法。
4)運(yùn)行本機(jī) C/C++ 應(yīng)用程序。將運(yùn)行應(yīng)用程序以查看它是否正常工作。我們還將討論一些用于處理常見錯誤的技巧。
☆
第一步:編寫Java代碼

jnidemo.CCallJava.java
package jnidemo;
public class CCallJava {
public static int showInfo(){
System.out.println("Happy New Year!");
return 2008;
}
public static void main(String args[]){
showInfo();
}
}
☆
第二步:編譯Java代碼
javac CCallJava.java
☆
編寫c代碼
1)開啟VC
2)新建一個Win32 Console Application工程,如果建立的是Win32 Application工程會報(bào)錯[LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16]
3)在Source Files下新建文件cCallJava.c
4)將jvm.lib加入到連接中,project->Settings->Link加入jvm.lib,同時配置Tools->Directories->Library files中加入jvm.lib目錄,例如:
C:\Program Files\Java\jdk1.6.0\lib
5)配置Include路徑,Tools->Directories->Include files加入java的include目錄,例如:
C:\Program Files\Java\jdk1.6.0\include
C:\Program Files\Java\jdk1.6.0\include\win32

cCallJava.c
#include <jni.h>
#include <stdio.h>
#ifdef _WIN32
#define PATH_SEPARATOR ';'
#else
#define PATH_SEPARATOR ':'
#endif
int main(){
JavaVMOption options[1];
JNIEnv *env;//JNI執(zhí)行環(huán)境
JavaVM *jvm;//Java虛擬機(jī)
JavaVMInitArgs vm_args;//初始化JVM的各種參數(shù)
long status;
jclass cls;
jmethodID mid;
jint result;
options[0].optionString = "-Djava.class.path=.";
memset(&vm_args, 0, sizeof(vm_args));
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = options;
status = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (status != JNI_ERR){
cls = (*env)->FindClass(env, "jnidemo/CCallJava");
printf("getCls\n");
if(cls !=0){
mid = (*env)->GetStaticMethodID(env, cls, "showInfo", "()I");
printf("getMid\n");
if(mid !=0){
printf("callCls\n");
result=(*env)->CallStaticIntMethod(env, cls, mid);
printf("call over result=%d\n",result);
}
}
(*jvm)->DestroyJavaVM(jvm);
printf("success!");
return 0;
}
else{
printf("error!");
return -1;
}
}
☆
執(zhí)行
1)在編譯的exe文件目錄下新建jnidemo文件夾,把CCallJava.class拷貝到此目錄下;
2)執(zhí)行exe文件

執(zhí)行結(jié)果
D:\jni\callJava\Debug>callJava.exe
getCls
getMid
callCls
Happy New Year!
call over result=2008
success!
☆
參考資料
JNI的API參考手冊[中文版]
IBM開發(fā)中心JNI教程
posted on 2008-02-15 18:20
破繭而出 閱讀(2075)
評論(1) 編輯 收藏 所屬分類:
Java 、
C/C++