一、JNI简介
JNI:Java Native Interface,是Java语言提供的一种通用接口,用于Java代码与本地化代码的交互。所谓本地化代码是指直接编译成的与机器相关的二进制代码,而非Java字节码之类的中间代码。Windows下面的可执行文件,DLL等,Linux下面的可执行文件和SO文件等,都是二进制代码。
JNI允许Java语言编写的程序与其他语言编写的程序库(DLL, SO)或可执行文件进行互操作,包括汇编、C、C++。JNI产生的原因在于以下几种需求:
(1)、你的应用程序需要使用系统相关的功能,而Java代码不支持或是难以办到。这个比较典型的是实现托盘图标,有几种现成的方案都是用的JNI做的,名字好像是叫做TrayIcon和StayOnTop。当然啦,如果是用的Java1.6,那就要另当别论了。
(2)、已有其他语言写好的类库或程序,希望Java程序可以使用它们。
(3)、出于更高的性能要求,希望使用汇编或是C/C++语言来实现部分功能。
下图出自JNI Tutorial,展示了JNI的地位:
二.在JAVA中调用C++的基本步骤
(1) 编写带有native关键字的方法的java类
(2) 用javac编译该Java类文件
(3) 使用javah命令行处理编译后的*.class文件,使之生成c/c++头文件
(4) 使用c/c++实现本地方法
(5) 编译c/c++文件生成动态链接库
(6) 将生产的*.dll放在c:\windows\system32目录下
下面详解这些步骤:
(1)不用说了,就是在java中写一个带有native的方法。如:private native void sum(int x, int y);
(2)进入类所在的目录,用javac进行编译,生成.class文件。
(3)这一步很重要,很多时候都生不成c++的头文件,总是提示找不到相应的类,这时,你得检查所配置的环境变量.如果还是不行,就使用下面的命令:javah -classpath . jni com.chnic.service.business,这个命令一般情况下都会好使。
com.chnic.service是包名,business是类名。测试的类的目录是:C:/Documents and Settings/Administrator/workspace/JNI2/src/com/chnic/service下。这样你编译生成的C++头文件会出现在src目录下。
(4)打开VC++6.0建立Win32 Dynamic-Link Library.然后把你刚才生成的头文件copy到工程的目录下。例如我新建的工作目录是:D:/Program Files/Microsoft Visual Studio/MyProjects/FruitFactory, 同时还要把jni.h,jni_md.h复制到这个目录下,因为你的C++头文件中包含了这两个头文件。它们可以从jdk目录中找到,C:/Program Files/Java/jdk1.6.0_21/include目录下和C:/Program Files/Java/jdk1.6.0_21/include/win32目录下。然后你就可以在C++中实现你想要的功能了。
(5)编译,运行,生成动态链接库,例如:Business.dll
(6)然后把动态链接库放到c:/windows/system32下。这样在java中就可以执行C++中的函数了。
sample - 我还没看懂
package com.chnic.service;
public class Business {
public Business(){}
static {
System.loadLibrary("FruitFactory");
}
public native double getPrice(String name);
public native Order getOrder(String name,int amount);
public native Order getRamdomOrder();
public native static void analyzeOrder(Order order);
public void notification(){
System.out.println("Got a notification");
}
public static void notificationByStatic(){
System.out.println("Got a notification in a static method");
}
}
java代码:
package com.chnic.service;
public class Order {
private String name="Fruit";
private double price;
private int amount=30;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
}
c++代码:
#include <iostream.h>
#include <string.h>
#include "com_chnic_service_Business.h"
jobject getInstance(JNIEnv* env,jclass obj_class);
JNIEXPORT jdouble JNICALL Java_com_chnic_service_Business_getPrice
(JNIEnv* env, jobject obj, jstring name){
const char* pname=env->GetStringUTFChars(name,NULL);
if(strcmp(pname,"Apple")==0){
env->ReleaseStringUTFChars(name,pname);
cout<<"After release:"<<pname<<endl;
return 1.2;
}
else{
env->ReleaseStringUTFChars(name,pname);
cout<<"After release:"<<pname<<endl;
return 2.1;
}
}
JNIEXPORT jobject JNICALL Java_com_chnic_service_Business_getOrder
(JNIEnv* env, jobject obj, jstring name, jint amount){
jclass order_class=env->FindClass("com/chnic/service/Order");
jobject order=getInstance(env,order_class);
jmethodID setName_method=env->GetMethodID(order_class,"setName","(Ljava/lang/String;)V");
env->CallVoidMethod(order,setName_method,name);
jmethodID setAmount_method=env->GetMethodID(order_class,"setAmount","(I)V");
env->CallVoidMethod(order,setAmount_method,amount);
return order;
}
JNIEXPORT jobject JNICALL Java_com_chnic_service_Business_getRamdomOrder
(JNIEnv* env, jobject obj){
jclass business_class = env->GetObjectClass(obj);
jobject business_obj = getInstance(env, business_class);
jmethodID notification_method = env->GetMethodID(business_class, "notification", "()V");
env->CallVoidMethod(business_obj, notification_method);
jclass order_class=env->FindClass("com/chnic/service/Order");
jobject order=getInstance(env,order_class);
jfieldID amount_field=env->GetFieldID(order_class,"amount","I");
jint amount=env->GetIntField(order,amount_field);
cout<<"amount:"<<amount<<endl;
return order;
}
JNIEXPORT void JNICALL Java_com_chnic_service_Business_analyzeOrder
(JNIEnv* env, jclass cls, jobject obj){
jclass order_class = env->GetObjectClass(obj);
jmethodID getName_method = env->GetMethodID(order_class, "getName", "()Ljava/lang/String;");
jstring name_str = static_cast<jstring>(env->CallObjectMethod(obj, getName_method));
const char* pname = env->GetStringUTFChars(name_str, NULL);
cout << "Name in Java_com_chnic_service_Business_analyzeOrder: " << pname << endl;
jmethodID notification_method_static = env->GetStaticMethodID(cls, "notificationByStatic", "()V");
env->CallStaticVoidMethod(cls, notification_method_static);
}
jobject getInstance(JNIEnv* env, jclass obj_class)
{
jmethodID construction_id = env->GetMethodID(obj_class, "<init>", "()V");
//得到构造方法的ID
jobject obj = env->NewObject(obj_class, construction_id);
//根据构造方法得到相应的类的对象
return obj;
}
主要解释一下C++中的方法:
GetStringUTFChars:把字符串类型(jstring)转换成char。
ReleaseStringUTFChars:释放字符串空间。因为创建时需要分配内存,所以释放一下。
FindClass:根据类名找到相应的类对象。需要完整的包名+类名。
GetObjectClass:传入的参数是对象,根据对象找到相应的class.
GetFieldID,GetMethodID:找到相应的成员变量与成员方法的ID。由于对成员变量的取值与设置值分成两个步骤,第一步是得到相应的ID,第二步是根据ID号取值或设置值。传入的参数有:class,成员变量(方法名),变量(方法签名)。签名可由:javap -s 类名(business)得到。
GetIntField得到相应变量的值,传入的参数是obj,与变量的id.
GetStaticIntField得到静态变量的值。传入的参数是class,变量的id.
SetIntField设置变量的值。
SetStaticIntField设置静态变量的值。
GetObjectField,SetObjectField如果想得到字符串的值,与设置字符串的值。如:
jfieldID fid;//store the field ID
jstring jstr;
const char* str;
jclass cls=env->GetObjectClass(obj);//get class
fid=env->GetFieldID(cls,"s","Ljava/lang/String;");
if(fid==NULL){
return;
}
jstr=(jstring)(env->GetObjectField(obj,fid));//分为两步,第一步得到的是field
//的id,第二步才能得到field的值
str=env->GetStringUTFChars(jstr,NULL);//转换成char类型
if(str==NULL){
return;
}
cout<<"In C:"<<endl;
cout<<"c.s="<<str<<endl;
//创建一个string然后赋值给field
jstr=env->NewStringUTF("123");
if(jstr==NULL){
return;
}
env->SetObjectField(obj,fid,jstr);//给这个field赋值
CallVoidMethod:调用非静态方法,传入的参数是obj,方法的ID。
CallStaticVoidMethod:调用的是静态方法,传入的参数是class,方法的ID。
NewStringUTF(string str):用来构造一下字符串对象
关于JNI基本的内容就介绍到这了。