【JNI】JNI环境搭建

news/2025/10/5 11:31:41/文章来源:https://www.cnblogs.com/zhyan8/p/19123018

1 前言

​ JNI (Java Native Interface) 是 JDK 提供的一种机制,用于实现 Java 代码与其他语言(主要是 C 和 C++)编写的本地代码之间的交互。

​ JNI 接口详见 JDK 安装目录中的 include/jni.h 文件,Android NDK 对 JDK 的 JNI 进行了扩展,对应的 jni.h 文件路径如下。

SDK\ndk\27.2.12479018\toolchains\llvm\prebuilt\windows-x86_64\sysroot\usr\include\jni.h

​ JNI 官方文档见 → https://docs.oracle.com/en/java/javase/17/docs/specs/jni/index.html。

img

2 在命令行中编译代码

2.1 环境准备

1)安装 JDK

​ 下载 JDK(建议下载 x64 Compressed Archive 文件),解压后将根目录写入 JAVA_HOME 环境变量中,如下。

img

​ 然后将 %JAVA_HOME%、%JAVA_HOME%\bin 加入 Path 环境变量中,如下。

img

​ 在命令行分别输入 java --version、 javac --version,如果有打印版本号,说明 JDK 环境搭建成功。

2)安装 MinGW

​ MinGW(Minimalist GNU for Windows)是一个用于 Windows 平台的轻量级 GNU 开发环境,它允许开发者在 Windows 上使用 GCC(GNU Compiler Collection)工具链编译本地 Windows 应用程序。

​ 下载 MinGW,解压后将根目录下的 bin 目录写入 Path 环境变量中。

img

​ 在命令行输入 gcc --version、g++ --version, 如果有打印版本号,说明 MinGW 环境搭建成功。

2.2 一个简单的案例

1)编写 java 代码

​ 用记事本编辑以下代码,保存为 HelloJNI.java。

​ HelloJNI.java

/*** 1. 生成字节码和头文件*    javac -encoding UTF-8 -h ./ HelloJNI.java (或 javah -encoding UTF-8 ./ HelloJNI.java, jdk10 之前)* * 2. 生成 dll 或 so*    g++ -fPIC -I"%JAVA_HOME%\\include" -I"%JAVA_HOME%\\include\\win32" -shared -o hello.dll HelloJNI.c* * 3. 运行*    java HelloJNI*/
public class HelloJNI {static {// 运行时加载 native 库, hello.dll (Windows) 或 hello.so (Linux/Unix/Android)System.loadLibrary("hello");}private native void sayHello();public static void main(String[] args) {new HelloJNI().sayHello();}
}

2)生成字节码和头文件

​ 在命令行执行以下命令,生成字节码(.class 文件)和头文件(.h 文件)。

// jdk10 及之后
javac -encoding UTF-8 -h ./ HelloJNI.java
// jdk10 之前
javah -encoding UTF-8 ./ HelloJNI.java

​ 执行完以上命令后,会生成 HelloJNI.class 和 HelloJNI.h 文件。HelloJNI.h 文件内容如下。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloJNI */#ifndef _Included_HelloJNI
#define _Included_HelloJNI
#ifdef __cplusplus
extern "C" {
#endif
/** Class:     HelloJNI* Method:    sayHello* Signature: ()V*/
JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *, jobject);#ifdef __cplusplus
}
#endif
#endif

3)编写 C 代码

#include <jni.h>
#include <stdio.h>
#include "HelloJNI.h"JNIEXPORT void JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject thisObj) {printf("Hello World!\n");return;
}

4)生成 dll 或 so

​ 在命令行执行以下命令,生成 ddl(或 so)文件。

// 生成 dll 文件 (Windows)
g++ -fPIC -I"%JAVA_HOME%\\include" -I"%JAVA_HOME%\\include\\win32" -shared -o hello.dll HelloJNI.c// 生成 so 文件 (Linux/Unix/Android)
g++ -fPIC -I"%JAVA_HOME%\\include" -I"%JAVA_HOME%\\include\\win32" -shared -o hello.so HelloJNI.c

5)运行代码

​ 在命令行执行以下命令,运行代码。

java HelloJNI

​ 运行结果如下。

Hello World!

6)一键编译运行脚本

@echo off
set class_name=HelloJNI
set native_file_name=HelloJNI
set lib_name=hello:: 删除缓存文件
del %class_name%.class
del %class_name%.h
del %lib_name%.dll:: 生成字节码和头文件
javac -encoding UTF-8 -h ./ %class_name%.java:: 生成 dll 或 so
g++ -fPIC -I"%JAVA_HOME%\\include" -I"%JAVA_HOME%\\include\\win32" -shared -o %lib_name%.dll %native_file_name%.c:: 运行
java %class_name%
pause

3 在 Android Studio 中编译代码

​ 在 Android Studio 中,依此选择 New -> New Project -> Native C++,创建 Native C++ 项目。

img

​ 也可以通过 New -> New Module -> Android Native Library,创建 Native Library。

img

​ 对于普通的 Android 项目,可以通过以下配置,使其支持 JNI。

1)build.gradle

defaultConfig {ndk {abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'}
}externalNativeBuild {cmake {path file('src/main/cpp/CMakeLists.txt')version '3.22.1'}
}

​ abiFilters 的作用是指定应用支持的 ABI(Application Binary Interface) 架构类型,即应用只会为指定的 CPU 架构生成原生库(.so 文件),而忽略其他架构。如果未配置 abiFilters,Gradle 默认会构建所有支持的 ABI(取决于 NDK 版本和项目依赖)。

armeabi-v7a: 32 位 ARM 架构 (较旧的 Android 设备)
arm64-v8a: 64 位 ARM 架构 (现代主流设备,性能更好)
x86: 32 位 Intel 架构 (模拟器或少数平板设备)
x86_64: 64 位 Intel 架构 (模拟器或高端设备)

2)创建 cpp 目录

img

3)C/C++ 代码

​ 如果用 C 语言实现,代码如下。

​ demo.c

#include <jni.h>
#include <string.h>JNIEXPORT jstring JNICALL
Java_com_zhyan8_test_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {return (*env)->NewStringUTF(env, "Hello from C");
}

​ 如果用 C++ 语言实现,代码如下。

​ demo.cpp

#include <jni.h>
#include <string>extern "C" JNIEXPORT jstring JNICALL
Java_com_zhyan8_test_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {return env->NewStringUTF("Hello from C++");
}

​ 可以看到,C 与 C++ 的主要区别如下。

  • 头文件:C 中 string 的引用是 #include <string.h>,C++ 中 string 的引用是 #include
  • extern "C":C++ 中多了 extern "C" 修饰,表示使用 C 风格的函数链接,禁止 C++ 的名称修饰。如果不禁用名称修饰,C++ 编译器会对函数名进行修饰,以支持函数重载等特性,会导致 Java 无法查找到本地函数。因此,使用 extern "C" 的函数不能重载(因为名称不再修饰)。
  • env:C 中的 env 是二级指针,所以访问函数通过 (*env)-> 访问;C++ 中的 env 是一级指针,所以访问函数通过 env-> 访问。

4)CMakeLists.txt

# 设置最小CMake版本
cmake_minimum_required(VERSION 3.22.1)# 声明项目名, 可以通过${PROJECT_NAME}访问, 在顶层CMakeLists.txt中, 也可以通过${CMAKE_PROJECT_NAME}访问
project("hello")# 设置源码包路径 (相对于当前CMakeLists.txt的路径)
set(PACKAGES_DIR "com/zhyan8/demo")# 头文件目录列表 (便于以该路径为相对路径访问头文件)
include_directories(${PACKAGES_DIR})# 源文件列表 (相对于当前CMakeLists.txt的路径)
file(GLOB_RECURSE SOURCES ./${PACKAGES_DIR} *.cpp)# 添加预构建库, SHARED用于将该库声明为一个shared library
# 在Java/Kotlin中, 需要通过System.loadLibrary()加载该库
add_library(${CMAKE_PROJECT_NAME} SHARED ${SOURCES})# 添加链接的三方库文件
target_link_libraries(${CMAKE_PROJECT_NAME}# 链接到目标库的库文件androidlog)

5)MainActivity.java

package com.zhyan8.test;import androidx.appcompat.app.AppCompatActivity;import android.os.Bundle;
import android.util.Log;public class MainActivity extends AppCompatActivity {static {System.loadLibrary("hello");}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.i("MainActivity", stringFromJNI());}public native String stringFromJNI();
}

6)打印日志

​ 如果想在 JNI 中使用 Android 中的日志类打印日志,可以使用以下代码。

#include <jni.h>
#include <android/log.h>#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "JNI_TAG", __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "JNI_TAG", __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, "JNI_TAG", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "JNI_TAG", __VA_ARGS__)extern "C"
JNIEXPORT void JNICALL
Java_com_zhyan8_test_MainActivity_testLog(JNIEnv* env, jobject /* this */) {LOGD("value1=%d", true);LOGI("value2=%d", 100);LOGW("value3=%f", 3.5f);LOGE("value4=%s", "abcd");
}

​ 声明:本文转自【JNI】JNI环境搭建。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/928196.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CS自学笔记

上一次那么郑重其事地打开博客园,打算记录一些什么的时候,还是高二牛客多校的记录。 那时的我还不知道几个月后我会因为保守策略差一点再次失去省一; 还不知道之后社交关系崩坏的种种; 还不知道首考的跌宕起伏; 还…

JDBC - Wrapper接口

java.sql.Wrapper javax.sql.DataSource java.sql.Connection java.sql.Statement java.sql.ResultSet java.sql.ResultSetMetaData java.sql.DatabaseMetaData java.sql.ParameterMetaData

asp做网站基本过程商标设计图片

在之前写过一篇关于虚拟环境使用的文章&#xff0c;但是还没有好好的介绍一下 Python 的用户环境&#xff0c;原因是自己一直没遇到要使用 用户环境 的使用场景&#xff0c;所以就一直懒得写。恰巧这两天&#xff0c;自己遇到了一个使用用户环境的体验可以完爆虚拟环境的案例&a…

深入解析:[论文阅读]Poisonprompt: Backdoor attack on prompt-based large language models

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

fasthx fastapi服务器端渲染看框架

fasthx fastapi服务器端渲染看框架fasthx 是一个fastapi 的服务器端渲染框架 包含的特性基于装饰器的语法 内置htmx 支持任何模版引擎,htmy,jinja2,dominate 对于装饰器路路由渲染引擎支持访问所有依赖 支持异步以及…

vue: 报错: vue ResizeObserver loop completed with undelivered notifications.

一,报错信息: vue ResizeObserver loop completed with undelivered notifications. 如图:我没有专门截图,转一张别人发的二,原因 ResizeObserver 警告解释这条警告源于 ResizeObserver 的实现机制。它表示监听了 D…

成都 企业 网站建设计算机多媒体辅助教学网站开发

文章目录 前言一、Bean 的作用域1.1 被修改的 Bean 案例1.2 作用域的定义1.3 Bean 的六种作用域1.4 Bean 作用域的设置 二、Spring 的执行流程 和 Bean 的生命周期2.1 Spring 的执行流程2.2 Bean 的生命周期2.3 Bean 生命周期的演示 前言 Bean 是 Spring 框架中的一个核心概念…

ds调度mssql多个T-SQL语句同步阻塞实现

ds调度mssql多个T-SQL语句,默认异步非阻塞,发送到mssql服务器即认为任务成功,立马开始节点的下一个任务;导致依赖失效,数据产出异常,上一个任务没正常跑完,下一个任务就开始了; 解决方案: 多个T-SQL语句,包装…

站点建设网站线上平面设计兼职

在java中&#xff0c;每个class都有一个相应的Class对象&#xff0c;当编写好一个类&#xff0c;编译完成后&#xff0c;在生成的.class文件中&#xff0c;就产生一个Class对象&#xff0c;用来表示这个类的类型信息。获得Class实例的三种方式&#xff1a; 1). 利用对象调用get…

个人专业制作网站html做网站需要服务器吗

昨天想着看一些图形学方面的知识&#xff0c;在CSDN上看到说Gabor函数可以精确是被图形细微处。于是从网上找了下面这么一篇文章看看&#xff1a; 二、Gabor函数 Gabor变换属于加窗傅立叶变换&#xff0c;Gabor函数可以在频域不同尺度、不同方向上提取相关的特征。另外Gabor函数…

福州网站快速排名提升凡客诚品市场份额

摘要&#xff1a;在看别人单片机程序时&#xff0c;你也许是奔溃的&#xff0c;因为全局变量满天飞&#xff0c;不知道哪个在哪用了&#xff0c;哪个表示什么&#xff0c;而且编写极其不规范。自己写单片机程序时&#xff0c;也许你也是奔溃的。总感觉重新开启一个项目&#xf…

C# 文档 I/O 操作详解:从基础到高级应用

C# 文档 I/O 操作详解:从基础到高级应用pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "M…

做企业网站需要哪些材料摄影的网站设计特点

使用HTML,CSS及JS实现注册功能中密码强度的显示&#xff0c;根据输入的密码判断并显示用户输入密码的强度等级是高还是低等。 效果演示 代码演示 html内容 <div class"container"><div class"userName-wrapper"><label for"userN…

想学做宝宝食谱上什么网站做软装找产品上哪个网站

概念ARQ:自动重传请求(Automatic Repeat-reQuest,ARQ)是OSI模型中数据链路层的错误纠正协议之一.RTO:Retransmission TimeOutFEC:Forward Error Correctionkcp简介kcp是一个基于udp实现快速、可靠、向前纠错的的协议&#xff0c;能以比TCP浪费10%-20%的带宽的代价&#xff0c;换…

wordpress微信站wordpress tar.xz

以垂直列表形式显示的菜单。 说明&#xff1a; 该组件从API Version 9开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 Menu组件需和bindMenu或bindContextMenu方法配合使用&#xff0c;不支持作为普通组件单独使用。 子组件 包含MenuIt…

2025提升门厂家最新企业品牌推荐排行榜,保温提升门,钢质提升门,消防提升门,分段式提升门,工业提升门公司推荐!

在现代工业高速发展的浪潮下,物流枢纽的高效运转、航空航天基地的精密作业,都离不开提升门这一关键基础设施。它不仅是空间通行的门户,更是保障行业安全高效运行的重要防线。然而,当下提升门市场品牌林立,产品质量…

360网站怎么做定制图片软件

目录 排序算法&#xff08;Sorting algorithm&#xff09;:) 一、定义 二、分类 三、评价标准 排序算法&#xff08;Sorting algorithm&#xff09;:) 一、定义 所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的…

高考数学易错考点02 | 临阵磨枪 - 指南

高考数学易错考点02 | 临阵磨枪 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mon…

知名建站的公司vi视觉设计手册

centos7&#xff0c;vbox复制出来的虚拟机无法ping通。其实这是vbox低版本没有解决的一个小问题&#xff0c;复制过去&#xff0c;主机的mac重新生成了(复制时候一定要选这个选项),但是配置文件的mac还是被复制的主机配置&#xff0c;网上搜索很多方案各种改&#xff0c;其实只…

培训销售网站建设wordpress安装提示500

礼帽运算是用原始图像减去其开运算图像的操作。礼帽运算能够获取图像的噪声信息&#xff0c;或者得到比原始图像的边缘更亮的边缘信息。 例如&#xff0c;图 8-22 是一个礼帽运算示例&#xff0c;其中&#xff1a; 左图是原始图像。中间的图是开运算图像。右图是原始图像减开运…