JNI学习

目标

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

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

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

1.1 选择Dll模板

在这里插入图片描述

1.2 创建第一个dll项目

在这里插入图片描述

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

在这里插入图片描述

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
 * E-mail: 1456084073@qq.com
 */
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

在这里插入图片描述

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

创建MtkHidUsbBridge项目

3.1 导入JNI生成.h文件

3.2 创建MtkHidUsbBridge.cpp文件

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

右键 ->属性

在这里插入图片描述

在这里插入图片描述

上述无用的话,则配置所有:右键工程 根目录(https://so.csdn.net/so/search?q=根目录&spm=1001.2101.3001.7020)→ 属性 → 最上面修改为“所有配置”、“所有平台”

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

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

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

在这里插入图片描述

在这里插入图片描述

4、测试桥梁与java

MtkUsbJNI.java

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

/**
 * Created with IntelliJ IDEA.
 * 测试JNI
 * @author 广大菜鸟
 * @since 2022/7/13 22:08
 * E-mail: 1456084073@qq.com
 */
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
 * E-mail: 1456084073@qq.com
 */
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
 * E-mail: 1456084073@qq.com
 */
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
 * E-mail: 1456084073@qq.com
 */
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
 * E-mail: 1456084073@qq.com
 */
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
 * E-mail: 1456084073@qq.com
 */
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
 * E-mail: 1456084073@qq.com
 */
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
 * E-mail: 1456084073@qq.com
 */
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;
}

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

在这里插入图片描述

7.参考学习资料

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

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

Java类型签名
booleanZ
byteB
charC
shortS
intI
longL
floatF
doubleD
ObjectL+包名+分号
[][+类型签名
[][][[+类型签名
方法(参数签名)+返回值签名

对象类型 签名示例如下

Java类型签名
StringLjava/lang/String;
ObjectLjava/lang/Object;
String[][Ljava/lang/String;
int func(int i, Object object)(ILjava/lang/Object;)I
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

广大菜鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值