一步一步教会你JAVA中调用C++

来源:互联网 发布:java反斜杠替换 编辑:程序博客网 时间:2024/04/29 06:12

装载自:http://blog.csdn.net/chenjin_zhong/article/details/5870305

一、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命令处理类文件,生成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++中的函数了。

 

示例演示:

 

编译生成Business.dll,即可在JAVA下运行了。 
java代码:

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基本的内容就介绍到这了。

0 0
原创粉丝点击