☆
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
破茧而出 阅读(2057)
评论(1) 编辑 收藏 所属分类:
Java 、
C/C++