0
点赞
收藏
分享

微信扫一扫

JNI学习


目标

1、 Java通过JNI调用C++动态链接库Demo实现全过程,添加解耦,
包括一个Dll动态链接库调用另外一个动态链接库的内容

2、实现一个传递参数,并获取返回值的JNI项目版本

1、创建C++环境 – 目标功能

1.1 选择Dll模板

JNI学习_java

1.2 创建第一个dll项目

JNI学习_学习_02

1.3 新建一个头文件 WindowHidUsb.h 和一个WindowHidUsb.cpp

JNI学习_学习_03

1.4 修改visual studio dll类型

项目-》属性-》配置管理器-》Win32 改为x64

2、创建一个Java环境

2.1 创建类

package com.mtk.ups.agent.api.usb;

/**
* Created with IntelliJ IDEA.
* 测试JNI
* @author 广大菜鸟
* @since 2022/7/13 22:08
*/
public class MtkUsbJNI {

/**
* 使用dll返回设备数量
* @return 设备数量
*/

native int scanDevice();

}

2.2 用JNI将C++代码头文件生在Java项目源文件夹下

javah -jni -encoding utf-8 com.mtk.ups.agent.api.usb.MtkUsbJNI

JNI学习_c++_04

3、创建c++环境 – 桥梁功能

创建MtkHidUsbBridge项目

3.1 导入JNI生成.h文件

3.2 创建MtkHidUsbBridge.cpp文件

3.3 关联JNI生成的头文件对应的目录以及其他JNI头文件目录,添加到jdk下面

右键 ->属性

JNI学习_c++_05

JNI学习_java_06

上述无用的话,则配置所有:右键工程 根目录根目录&spm=1001.2101.3001.7020)→ 属性 → 最上面修改为“所有配置”、“所有平台”

添加头文件目录:右键工程根目录→ 属性 → C/C++ → 常规 → 附加包含目录,添加include目录。

JNI学习_学习_07

JNI学习_学习_08

JNI学习_学习_09

运行MtkHidUsbBridge.cpp,生成dll.将dll加入idea环境依赖:project structure -> Librarys -> +号

(后面生成dll都要以这种方式)

JNI学习_学习_10

JNI学习_开发语言_11

4、测试桥梁与java

MtkUsbJNI.java

package com.mtk.ups.agent.api.usb;

/**
* Created with IntelliJ IDEA.
* 测试JNI
* @author 广大菜鸟
* @since 2022/7/13 22:08

*/
class MtkUsbJNI {

/**
* 使用dll返回设备数量
* @return 设备数量
*/

native int scanDevice();

}

构造MtkUsb单例调用MtkUsbJNI的native方法

package com.mtk.ups.agent.api.usb;

/**
* Created with IntelliJ IDEA.
*
* @author 广大菜鸟
* @since 2022/7/13 23:53

*/
public class MtkUsb {

private static final MtkUsb mtkUsb = new MtkUsb();

private static final MtkUsbJNI mtkUsbJni = new MtkUsbJNI();

private MtkUsb(){}

public static MtkUsb getInstance(){
return mtkUsb;
}

public int scanDevice(){
return mtkUsbJni.scanDevice();
}
}

构造测试类

package com.mtk.ups.agent.api;

import com.mtk.ups.agent.api.usb.MtkUsb;

/**
* Created with IntelliJ IDEA.
*
* @author 广大菜鸟
* @since 2022/7/13 23:51

*/
public class MtkHidUsbTest {
public static void main(String[] args) {
System.loadLibrary("MtkHidUsbBridge");
System.out.println("Device count = "+ MtkUsb.getInstance().scanDevice());
}
}

5、修改目标dll和桥梁dll,实现解耦

5.1 在目标c++项目内

pch.h

// pch.h: 这是预编译标头文件。
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。

#ifndef PCH_H
#define PCH_H

// 添加要在此处预编译的标头
#include "framework.h"
#define API_DLL __declspec(dllexport)

#endif //PCH_H

WindowHidUsb.h

#pragma once
#include "pch.h"
API_DLL int scanDevice();

WindowHidUsb.cpp

#include"WindowHidUsb.h"
#include "pch.h"
API_DLL int scanDevice() {
return 2;
}

重新生成项目,将Release的.dll文件拷贝到java的src目录下

5.2 在Java项目内,增加dll导入语句

package com.mtk.ups.agent.api;

import com.mtk.ups.agent.api.usb.MtkUsb;

/**
* Created with IntelliJ IDEA.
*
* @author 广大菜鸟
* @since 2022/7/13 23:51

*/
public class MtkHidUsbTest {
public static void main(String[] args) {
System.loadLibrary("MtkHidUsb");
System.loadLibrary("MtkHidUsbBridge");
System.out.println("Device count = "+ MtkUsb.getInstance().scanDevice());
}
}

5.3 桥梁项目

5.3.1 拷贝目标项目的WindowHidUsb.h文件到桥梁项目,并修改内容

#pragma once
#include "pch.h"

#if defined(WIN32)
#if defined(Release)
#pragma comment(lib, "C:/Users/Lenovo/Desktop/JNIDemo/MtkHidUsb/x64/Release/MtkHidUsb.lib")
#pragma message("Automatically linking debug with MtkHidUsb.lib")
#else
#pragma comment(lib, "C:/Users/Lenovo/Desktop/JNIDemo/MtkHidUsb/x64/Debug/MtkHidUsb.lib")
#pragma message("Automatically linking debug with MtkHidUsb.lib")
#endif
#endif

int scanDevice();

5.3.2 修改 WindowHidUsbBridge.cpp

#include "pch.h"
#include "com_mtk_ups_agent_api_usb_MtkUsbJNI.h"
#include "WindowHidUsb.h"

JNIEXPORT jint JNICALL Java_com_mtk_ups_agent_api_usb_MtkUsbJNI_scanDevice
(JNIEnv*, jobject) {
//从底层库获取
return scanDevice();
}

5.3.3 生成新的WindowHidUsbBridge.dll文件,拷贝到java环境,加入依赖

6.进阶–传值

6.1 创建java项目

消息结构体

package com.demo.bean;

/**
* Created with IntelliJ IDEA.
*
* @author 广大菜鸟
* @since 2022/7/14 23:12

*/
public class ApiResult {
private int result;
private String description;

public int getResult() {
return result;
}

public void setResult(int result) {
this.result = result;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}
}

Native类

package com.demo.api;

import com.demo.bean.ApiResult;

/**
* Created with IntelliJ IDEA.
*
* @author 广大菜鸟
* @since 2022/7/14 23:14
*
*/
public class NativeApiJni {

native ApiResult getApiResult(int index);

}

编写单例模式 NativeApi

package com.demo.api;

import com.demo.bean.ApiResult;

/**
* Created with IntelliJ IDEA.
*
* @author 广大菜鸟
* @since 2022/7/14 23:17

*/
public class NativeApi {
private final NativeApiJni jni = new NativeApiJni();

private static final NativeApi api = new NativeApi();

private NativeApi(){}

public static NativeApi getInstance(){
return api;
}

public ApiResult getApiResult(int index){
return jni.getApiResult(index);
}
}

编写测试类

Test.java

package com.demo;


import com.demo.api.NativeApi;
import com.demo.bean.ApiResult;

/**
* Created with IntelliJ IDEA.
*
* @author 广大菜鸟
* @since 2022/7/14 23:16

*/
public class Test {
public static void main(String[] args) {
System.loadLibrary("ApiNativeDll");

ApiResult result = NativeApi.getInstance().getApiResult(0);
System.out.println(result);

result = NativeApi.getInstance().getApiResult(1);
System.out.println(result);
}
}

6.2 创建C++dll项目

ApiNativeDll.cpp

#include"pch.h"
#include"com_demo_api_NativeApiJni.h"
#include"ApiNativeDll.h" // 为空

JNIEXPORT jobject JNICALL Java_com_demo_api_NativeApiJni_getApiResult(JNIEnv* env, jobject, jint jIndex) {
//获取Java实例
jclass objectC1ass = env->FindClass("com/demo/bean/ApiResult");
//获取成员变量一构造函数
jmethodID objectC1assInitID = env->GetMethodID(objectC1ass, "<init>", "()V");
//创建Java对象
jobject objectNewApiResult = env->NewObject(objectC1ass, objectC1assInitID);
//获取属性
jfieldID resultField = env->GetFieldID(objectC1ass, "result", "I");
jfieldID descriptionField = env->GetFieldID(objectC1ass, "description", "Ljava/lang/String;");

env->SetIntField(objectNewApiResult, resultField, jIndex);
env->SetObjectField(objectNewApiResult, descriptionField, env->NewStringUTF(jIndex > 0 ? "Failure" : "Success"));
//或者 调用方法: (*env)->CallVoidMethod(env, obj, mid, depth);
return objectNewApiResult;
}

其余操作和之前一样,结果是

JNI学习_学习_12

7.参考学习资料

  1. 搭建基本jni项目
    ​​​ https://www.bilibili.com/video/BV1gq4y1x7Ae​​
  2. 传对象
    ​​​ https://www.bilibili.com/video/BV1444y1G76L​​
  3. java和c++对应类型签名

补充资料:
Java中的每个类型和方法,都对应一个唯一字符串,这个就是签名

Java类型

签名

boolean

Z

byte

B

char

C

short

S

int

I

long

L

float

F

double

D

Object

L+包名+分号

[]

[+类型签名

[][]

[[+类型签名

方法

(参数签名)+返回值签名

对象类型 签名示例如下

Java类型

签名

String

Ljava/lang/String;

Object

Ljava/lang/Object;

String[]

[Ljava/lang/String;

int func(int i, Object object)

(ILjava/lang/Object;)I


举报

相关推荐

0 条评论