JNI,java调用c++代码入门
发布日期:2021-06-29 15:52:31 浏览次数:2 分类:技术文章

本文共 8200 字,大约阅读时间需要 27 分钟。

JNI

JNI概述

JNI(java native interface),提供了java调用本地库(c/c++编写的生成的动态链接库.dll)的接口

java项目中,本地方法接口使用native字段标识

入门案例 基于Windows操作系统

  • java项目编写接口
package net.cn.console.natlib;public class Hello {
public native String hello();}
  • 生成头文件
    • 使用javah命令,如果找不到javah命令,则可能是新版本jdk将javah命令集成到javac命令里面
    • 使用 java -h <dir> <source>
      • dir 生成头文件所在目录名
      • source 源文件
javac -h jni net.cn.console.natlib.Hello.java

编译成功会在当前工作目录下的 jni目录下生成头文件net_cn_console_natlib_Hello.h

文件内容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */#include 
/* Header for class net_cn_console_natlib_Hello */#ifndef _Included_net_cn_console_natlib_Hello#define _Included_net_cn_console_natlib_Hello#ifdef __cplusplusextern "C" {#endif/* * Class: net_cn_console_natlib_Hello * Method: hello * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_net_cn_console_natlib_Hello_hello (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif
  • 新建一个c++项目,项目类型为动态链接库项目
  • 将生成的头文件导入到项目中
    • 除了生成的头文件,还需要以下文件
      • JAVA_HOME\include\jni.h
      • JAVA_HOME\include\win32\jni_md.h

这里为了方便直接将这两个头文件导入到当前项目中

在这里插入图片描述

如果net_cn_console_natlib_Hello.h文件报错,一般是头文件引入方式不正确引起

/* DO NOT EDIT THIS FILE - it is machine generated *///将这里的include 
修改为 include "jni.h"#include "jni.h"/* Header for class net_cn_console_natlib_Hello */#ifndef _Included_net_cn_console_natlib_Hello#define _Included_net_cn_console_natlib_Hello#ifdef __cplusplus
  • 新建源文件net_cn_console_natlib_Hello.cpp,实现net_cn_console_natlib_Hello.h中的方法
#include "pch.h" //预编译相关,vs报错后提示引入这个头文件#include "net_cn_console_natlib_Hello.h"#include 
#include
using namespace std;JNIEXPORT jstring JNICALL Java_net_cn_console_natlib_Hello_hello(JNIEnv*env, jobject) { cout << "this is print by c++"<
NewStringUTF((const char*)res); return value;}
  • 配置项目,生成dll,这里配置为releasex64
  • 项目右键,生成

在这里插入图片描述

​ 会生成jni-demo1.dll文件

  • 将生成的dll文件导入java项目中,这里使用idea

在这里插入图片描述

  • 编写测试代码
public class MainApp {
static {
System.loadLibrary("jni-demo1"); } public static void main(String[] args) {
Hello hello=new Hello(); final String hello1 = hello.hello(); System.out.println(hello1); }}

如果控制台成功打印以下内容,说明程序运行正常

this is print by c++hello JNI

linux环境

linux下动态链接库的后缀一般是.so (shared object)

使用gcc编译的话,直接将相关文件放在同一个目录下,使用下面命令编译成动态链接库(.so)

gcc -fPIC -shared net_cn_console_natlib_Hello.cpp -o  libdemo.so

jni常用操作

基本数据类型

本地方法中,java数据类型都会以j*的格式命名,其中基本数据类型和c++数据类型基本可以无缝衔接,

String

本地方法中,使用jstring表示java中的String类型,需要转换成c++的char*才能处理

  • jstring-> char*
const char * nametmp =env->GetStringUTFChars(name,NULL);
  • char* -> jstring
char str[]="hello jni";jstring result= env->NewStringUTF(str);

对象操作

对象操作和java中的反射比较类似

  • 获取类信息
jclass personClass = env->FindClass("com/pojo/User");//也可以使用下面的方法直接获取传入对象的类jclass personClass = env->GetObjectClass(person);
  • 获取/设置属性
    • 获取属性需要使用env的Get*Field()来进行,如GetIntField,GetObjectField…
    • 这个方法需要两个参数,一个是对象,另一个是属性的id
    • 属性id可以通过GetFieldID来获取
//获取属性idjfieldID nameId = env->GetFieldID(personClass,"name","Ljava/lang/String;");jfieldID ageId = env->GetFieldID(personClass,"age","I");//获取对象属性jstring name=static_cast
(env->GetObjectField(person,nameId));jint age = env->GetIntField(person,ageId);//设置对象属性env->SetObjectField(person,nameId,newName);env->SetIntField(person,ageId,age);

🔴 ⚠️ 这里有个大坑: 获取属性id时,"Ljava/lang/String;"这个参数最后面的分号“;”千万不能省略,必须加,否则就会找不到属性id

  • 执行方法
    • 执行方法需要使用Call*Method或者Call*MethodA*代表返回类型,没有A代表没有参数,有A代表需要参数
    • 需要方法Id,方法id使用GetMethodID方法获取
      • 这个方法需要传入类信息,方法名和参数签名
      • 参数签名示例
参数 返回值 参数签名字符串
void void ()V
int,String void (I;Ljava/lang/String;)V
void String ()Ljava/lang/String;

示例,调用对象的toString方法

jmethodID toStingId = env->GetMethodID(personClass,"toString","()Ljava/lang/String;");jstring user2Str = (jstring)env->CallObjectMethod(user2,toStingId);
  • 创建对象
    • 创建对象本质上也是在调用对象的方法,需要先获取构造方法id,再用NewObject创建对象
//使用无参数构造器创建对象jmethodID initMethodIdNoArgs = env->GetMethodID(personClass,"
","()V");jobject user1 = env->NewObject(personClass,initMethodIdNoArgs);//使用有参数构造创建对象 //创建参数数组jvalue argName;argName.l=env->NewStringUTF("zhangsna");jvalue argAge;argAge.i=55;jvalue args[]={argName,argAge};jmethodID initMethodIdWithArgs = env->GetMethodID(personClass,"
","(Ljava/lang/String;I)V");jobject user2= env->NewObjectA(personClass,initMethodIdWithArgs,args);

ℹ️ 上述示例中参数数组的数据类型是 jvaluejvalue是一个union,定义如下

typedef union jvalue {    jboolean z;    jbyte    b;    jchar    c;    jshort   s;    jint     i;    jlong    j;    jfloat   f;    jdouble  d;    jobject  l;} jvalue;

示例

java项目准备

一个User对象

package com.pojo;public class User {
private String tel; private int age; private String name; public User(){
} public User(String name, int age) {
this.name = name; this.age = age; } public String getName() {
return name; } public void setName(String name) {
this.name = name; } public int getAge() {
return age; } public void setAge(int age) {
this.age = age; } @Override public String toString() {
return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; }}

native方法类

public class JniDemo {
public native String hello(); public native int avg(int[] ints); public native void sort(int[] data ); public native User proc(User user);}

下面演示如何在c++项目中实现这些方法

  • hello(),返回一个字符串
/* * return a String "hello jni" * Class:     JniDemo2 * Method:    hello * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_JniDemo2_hello  (JNIEnv * env, jobject obj)  {    char str[]="hello jni";    jstring result= env->NewStringUTF(str);    return result;}
  • avg(int[] ints)
JNIEXPORT jint JNICALL Java_JniDemo2_avg  (JNIEnv * env, jobject obj, jintArray data)  {        jint length=0,sum=0;        length = env->GetArrayLength(data);        jint* ints = env->GetIntArrayElements(data,0);        for (size_t i = 0; i < length; i++)        {            sum+=ints[i];        }        return sum/length;  }
  • sort(int[] data ) 数组排序
JNIEXPORT void JNICALL Java_JniDemo2_sort  (JNIEnv * env, jobject obj, jintArray data)  {      jint len = env->GetArrayLength(data);      jint* value = env->GetIntArrayElements(data,0);          for (size_t i = 0; i < len; i++)      {          for (size_t j = i; j < len; j++)          {            if (value[i]
ReleaseIntArrayElements(data,value,0); }
  • proc(User user)
  • 方法传入一个User对象,本地方法修改其属性值;方法内部创建一个新的User对象并返回
JNIEXPORT jobject JNICALL Java_JniDemo2_proc(JNIEnv * env, jobject instace, jobject person){      jclass personClass = env->FindClass("com/pojo/User");    //也可以使用下面的方法直接获取传入对象的类    // jclass personClass = env->GetObjectClass(person);        cout<<"userClass "<
<
GetFieldID(personClass,"name","Ljava/lang/String;"); jfieldID ageId = env->GetFieldID(personClass,"age","I"); jstring name=static_cast
(env->GetObjectField(person,nameId)); // 修改传入的User对象 //modify name jboolean isCopy=0; // cout<<"char utf"<
GetStringUTFChars(name,NULL); cout<
<
NewStringUTF(name2); env->SetObjectField(person,nameId,nameJstr); env->ReleaseStringUTFChars(name,nametmp); // modify age jint age = env->GetIntField(person,ageId); age++; env->SetIntField(person,ageId,age); // 使用没有参数构造器创建一个User对象 jmethodID initMethodIdNoArgs = env->GetMethodID(personClass,"
","()V"); jmethodID initMethodIdWithArgs = env->GetMethodID(personClass,"
","(Ljava/lang/String;I)V"); jobject user1 = env->NewObject(personClass,initMethodIdNoArgs); env->SetIntField(user1,ageId,12); env->SetObjectField(user1,nameId,env->NewStringUTF("lisi")); // 使用有参数构造器创建一个User对象 jvalue argName; argName.l=env->NewStringUTF("zhangsna"); jvalue argAge; argAge.i=55; jvalue args[]={argName,argAge}; jobject user2= env->NewObjectA(personClass,initMethodIdWithArgs,args); jmethodID toStingId = env->GetMethodID(personClass,"toString","()Ljava/lang/String;"); jstring user2Str = (jstring)env->CallObjectMethod(user2,toStingId); const char* user2Chars= env->GetStringUTFChars(user2Str,0); cout<
<

参考资料

  • https://www.runoob.com/w3cnote/jni-getting-started-tutorials.html
  • https://blog.csdn.net/qhs1573/article/details/87704431
  • https://www.cnblogs.com/yongdaimi/p/14023154.html

转载地址:https://console.blog.csdn.net/article/details/116590605 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:linux命令行工具汇总
下一篇:SpringSecurity项目如何实现角色包含关系(角色继承)

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年04月05日 08时56分40秒