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

JavaCallC.java
package jnidemo;
public class JavaCallC {
/**
* native 關鍵字告訴 Java 編譯器:方法是用 Java 類之外的本機代碼實現
* 只能在 Java 類中聲明本機方法,而不能實現它
*/
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) {
/**
* 裝入了包含本機方法的實現的共享庫文件
* (基于 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
☆
第三步:創建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 聲明中的一般參數以外,所有這些函數的參數表中都有一個指向 JNIEnv 和 jobject 的指針。指向 JNIEnv 的指針實際上是一個指向函數指針表的指針,這些函數提供各種用來在 C 和 C++ 中操作 Java 數據的能力。
jobject 參數引用當前對象。因此,如果 C 或 C++ 代碼需要引用 Java 函數,則這個 jobject 充當引用或指針,返回調用的 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
不然編譯會報錯[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);
//獲取數組指針
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");
//新建數組
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++ 函數實現的比較
C 和 C++ 代碼幾乎相同;唯一的差異在于用來訪問 JNI 函數的方法。在 C 中,JNI 函數調用由“(*env)->”作前綴,目的是為了取出函數指針所引用的值。在 C++ 中,JNIEnv 類擁有處理函數指針查找的內聯成員函數。下面將說明這個細微的差異,其中,這兩行代碼訪問同一函數,但每種語言都有各自的語法。
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
【不知道哪里出問題,執行的時候一直報錯no libJavaCallC in java.library.path】
☆
第六步:運行JAVA程序
1)將libJavaCallC.dll放在環境變量的路徑中;
2)運行:java jnidemo.JavaCallC

運行結果
voidMethod called!
intMethod=4016
boolMethod=false
intArrayMethod=6
happy new year woxingwosu
☆
JNI[其他語言調用JAVA]
1)希望實現的這部分代碼是平臺無關的,它將用于跨多種平臺使用的功能。
2)需要在本機應用程序中訪問用 Java 語言編寫的代碼或代碼庫。
3)希望從本機代碼利用標準 Java 類庫
☆
開發步驟【摘】
1)編寫 Java 代碼。這個步驟包含編寫一個或多個 Java 類,這些類實現(或調用其它方法實現)您想要訪問的功能。
2)編譯 Java 代碼。在能夠使用這些 Java 類之前,必須成功地將它們編譯成字節碼。
3)編寫 C/C++ 代碼。這個代碼將創建和實例化 JVM,并調用正確的 Java 方法。
4)運行本機 C/C++ 應用程序。將運行應用程序以查看它是否正常工作。我們還將討論一些用于處理常見錯誤的技巧。
☆
第一步:編寫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工程會報錯[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執行環境
JavaVM *jvm;//Java虛擬機
JavaVMInitArgs vm_args;//初始化JVM的各種參數
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;
}
}
☆
執行
1)在編譯的exe文件目錄下新建jnidemo文件夾,把CCallJava.class拷貝到此目錄下;
2)執行exe文件

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