首页 > 编程 > C++ > 正文

通过JNI混合使用Java和C++

2019-11-11 04:23:18
字体:
来源:转载
供稿:网友

    这文章写的不错,

JNI(java Native Interface)是Java语言的一部分,可以访问非Java语言编写的程序,也可以用于在C++程序中执行Java代码。

步骤:

1>  编写带有native声明方法的Java类,并且该方法只定义不实现,后期由c++负责实现:

// HelloCpp.java

public class HelloCpp

{

    // ...

   public native void callCpp();

    // ...

}

 

2>  由于后期的C++实现代码最终会被编译为一个动态库.dll,因此需要在Java类中定义一个静态代码块,提前加载该动态库,假设动态的名字为hellocpp.dll:

// HelloCpp.java

public class HelloCpp

{

    static

    {

       System.loadLibrary("hellocpp");

    }

    public native void callCpp();

    // ...

}

 

3>  在Java类中定义main方法调用该native方法:

// HelloCpp.java

public class HelloCpp

{

    static

    {

       System.loadLibrary("hellocpp");

    }

    public native void callCpp();

    public static void main(String[] args)

    {

       System.out.PRintln("***** JNI Test *****");

       HelloCpp instance = new HelloCpp();

       instance.callCpp();      // 调用native方法

    }

}

 

4>  编译包含native方法的Java类,生成class字节码文件:

javac HelloCpp.java        // 生成HelloCpp.class

 

5>  生成与native方法对应的.h头文件:

javah –jni HelloCpp         // 生成HelloCpp类对于的头文件HelloCpp.h

 

// HelloCpp.h

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class HelloCpp */

#ifndef _Included_HelloCpp

#define _Included_HelloCpp

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     HelloCpp

 * Method:    callCpp

 * Signature: ()V

 */

JNIEXPORT void JNICALL Java_HelloCpp_callCpp(JNIEnv* env, jobject this);

#ifdef __cplusplus

}

#endif

#endif

 

6>  使用C++实现native方法:

// HelloCpp.cpp

#include "HelloCpp.h"

#include <jni.h>

#include <iostream>

 

JNIEXPORT void JNICALL Java_HelloCpp_callCpp(JNIEnv* env, jobject this)

{

    std::cout << "C++ Implementation" << std::endl;

}

 

7>  编译生成动态库hellocpp.dll:

g++ -Wl,--kill-at –shared –I D:/jdk1.7.0_75/include –I D:/jdk1.7.0_75/include/win32 HelloCpp.cpp –o hellocpp.dll

 

8>  调用hellocpp.dll来运行Java程序:

Java HelloCpp

结果如下:

***** JNI Test *****

C++ Implementation

 

说明:

JNIEXPORT void JNICALL Java_HelloCpp_callCpp(JNIEnv* env, jobject this);

JNIEXPORT和JNICALL是宏。

JNIEnv*指向一个位置,该位置包含一个指向函数表的指针,表中的每一项都是一个指向JNI函数的指针,native方法通过JNI函数访问JVM的中的数据 ,如下所示:

第二个参数对于非静态方法为jobject,对于静态方法为jclass。jobject表示调用native方法对象自身的引用,如同C++中的this指针;jclass表示定义native方法的类的引用。

 

如下介绍带有参数返回值的native方法:

// Prompt.java

复制代码
 1 class Prompt 2 { 3     static 4     { 5         System.loadLibrary("Prompt"); 6     } 7     private native String GetLine(String prompt); 8      9     public static void main(String[] args)10     {11         Prompt p = new Prompt();12         String input = p.GetLine("Enter a line:");13         System.out.println("Your Input is: " + input);14     }15 }     复制代码

 

// Prompt.h

复制代码
 1 /* DO NOT EDIT THIS FILE - it is machine generated */ 2 #include <jni.h> 3 /* Header for class Prompt */ 4  5 #ifndef _Included_Prompt 6 #define _Included_Prompt 7 #ifdef __cplusplus 8 extern "C" { 9 #endif10 /*11  * Class:     Prompt12  * Method:    GetLine13  * Signature: (Ljava/lang/String;)Ljava/lang/String;14  */15 JNIEXPORT jstring JNICALL Java_Prompt_GetLine(JNIEnv* env, jobject _this, jstring prompt);16 17 #ifdef __cplusplus18 }19 #endif20 #endif复制代码

 

// Prompt.cpp

复制代码
 1 #include "Prompt.h" 2 #include <iostream> 3  4 JNIEXPORT jstring JNICALL Java_Prompt_GetLine(JNIEnv* env, jobject _this, jstring prompt) 5 { 6     char buf[1024]; 7     const char* str; 8     str = env->GetStringUTFChars(prompt, NULL);            /* 获得传入的字符串,将其转换为native Strings */ 9     if(str == NULL)                                        /* str == NULL意味着JVM为native String分配内存失败 */10     {11         return NULL;12     }13     std::cout << str;                                        /* 显示传入的字符串参数 prompt */ 14     env->ReleaseStringUTFChars(prompt, str);                /* 通知JVM释放String所占的内存 */15     16     std::cin.get(buf, 1024);17     return env->NewStringUTF(buf);                        /* 构造新的Java.lang.String,如果JVM分配内存失败,则抛出OutOfMemoryError,并且返回NULL */18 }复制代码

 

补充信息:

:: UTF-8字符串以’/0’结尾,而Unicode字符串则不是。如果需要获得Unicode格式的jstring的长度,可以使用GetStringLength;如果需要获得UTF-8格式的jstring的长度,可以先使用GetStringUTFChars,在其结果上使用strlen,或者直接使用GetStringUTFLength

:: GetStringUTFChars的第二个参数为jboolean *isCopy,其指向分配的内存空间,如果isCopy被设为JNI_TRUE,那么返回的String是Java String的一个副本;如果被设为JNI_FALSE,那么返回一个指向Java String本身的指针,此时不允许修改返回的String。

:: 函数对Get/ReleaseStringCritical的作用于Get/ReleaseStringChars类似,但是对于程序员而言,该函数对之间的代码相当于“临界区”。在该“临界区”内,native代码不能调用任何的JNI函数,否则将引起当前线程阻塞。

:: GetStringRegion/GetStringUTFRegion将Unicode格式的String复制到预分配的缓冲区中,由于不需要JVM分配内存,因此也就不需要释放操作:

按 Ctrl+C 复制代码JNIEXPORT jstring JNICALL Java_Prompt_GetLine(JNIEnv* env, jobject _this, jstring prompt){    char inbuf[1024], outbuf[1024];    int len = env->GetStringUTFLength(prompt);    env->GetStringUTFRegion(prompt, 0, len, outbuf);    std::cout << outbuf;        std::cin.get(inbuf, 1024);    return env->NewStringUTF(inbuf);    }

 

总结:

1>  数据类型对应关系表:

Java 类型

本地 C 类型

实际表示的 C 类型(Win32)

boolean

jboolean

unsigned char

byte

jbyte

signed char

char

jchar

unsigned short

short

jshort

short

int

jint

long

long

jlong

__int64

float

jfloat

float

double

jdouble

double

void

void

N/A

 

2>  JNI字符串函数

JNI函数

描述

版本

GetStringChars

ReleaseStringChars

获得/释放一个Unicode格式的字符串指针,可能返回一个字符串的副本

JDK 1.1

GetStringUTFChars

ReleaseStringUTFChars

获得/释放一个UTF-8格式的字符串指针,可能返回一个字符串的副本

JDK 1.1

GetStringLength

返回Unicode格式字符串的长度

JDK 1.1

GetStringUTFLength

返回UTF-8格式字符串的长度

JDK 1.1

NewString

根据Unicode格式的C字符串创建一个Java字符串

JDK 1.1

NewStringUTF

根据UTF-8格式的C字符串创建一个Java字符串

JDK 1.1

GetStringCritical

ReleaseStringCritical

获得/释放一个Unicode格式的字符串指针,可能返回一个字符串的副本【在该函数对区间内,不能使用任何JNI函数】

JDK 1.2

GetStringRegion

将Unicode格式的String复制到预分配的缓冲区中

JDK 1.2

GetStringUTFRegion

将UTF-8格式的String复制到预分配的缓冲区中

JDK 1.2

备注:

首先,设置项目依赖的本地库,例如把opencv-246.jar引入到项目的lib库中,opencv-246.jar在opencv的安装目录下的/build/java下。

其次,设置项目的native库,把opencv_java246.dll文件所在的目录加入到Native library location中,opencv_java246.dll在opencv安装目录下的/build/java/x86或/build/java/x64中,前者是32位系统用的,后者64位。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表

图片精选