1. JNI开发环境搭建与Hello World

JNI开发环境搭建与Hello World 学习卡片

环境搭建目标与工具链组成

本案例要在 Linux 或 Windows 上完成一条最小可用的 JNI 工具链:JDK 提供 javac 与 java,NDK 提供交叉编译器与 JNI 头文件,构建工具负责把 C 源文件编译成动态库,最终由 JVM 在运行时加载该库并调用本地函数。三者缺一不可,且版本必须匹配。

组件作用本案例验证点
JDK 17编译 Java 类、运行 JVM、提供 jni.hjava -version 与 javac -version 一致
NDK r26提供 clang 与平台头文件clang --version 可执行
CMake 3.22+生成构建脚本,定位 NDK 工具链cmake --version 满足最低要求

工具链关系与调用路径

JDK 17 javac / java / jni.h JAVA_HOME 已配置 NDK r26 clang / sysroot ANDROID_NDK_HOME CMake 3.22+ 构建脚本生成 toolchain 定位 HelloJNI.java native String sayHello() System.loadLibrary("hello") hello.c JNIEXPORT jstring JNICALL Java_HelloJNI_sayHello libhello.so 动态库产物 JVM 运行时加载 JVM 加载 libhello.so 并调用 sayHello() 输出: Hello from JNI!

Linux 环境配置与验证

以下命令在 Ubuntu 22.04 上验证通过。先确认 JDK 与 NDK 路径,再设置环境变量。

# 1. 检查 JDK
java -version
javac -version

# 2. 设置 JAVA_HOME(根据实际路径调整)
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export PATH=$JAVA_HOME/bin:$PATH

# 3. 设置 NDK 路径
export ANDROID_NDK_HOME=$HOME/android-ndk-r26
export PATH=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin:$PATH

# 4. 验证 clang 与 cmake
clang --version
cmake --version
若 clang --version 报错,说明 NDK 路径未加入 PATH;若 javac 找不到,检查 JAVA_HOME 是否指向 JDK 而非 JRE。

Windows 环境配置要点

Windows 上推荐使用 MSYS2 或 WSL2 获得与 Linux 一致的编译体验。若使用原生 PowerShell,需将 JDK 的 bin 与 NDK 的 toolchains\llvm\prebuilt\windows-x86_64\bin 加入系统 Path,并设置 JAVA_HOME 与 ANDROID_NDK_HOME 为不含空格的路径。

# PowerShell 验证
java -version
javac -version
clang --version
cmake --version

编写最小 JNI 程序

创建 HelloJNI.java,声明一个 native 方法并在 main 中调用:

public class HelloJNI {
    static {
        System.loadLibrary("hello");
    }

    private native String sayHello();

    public static void main(String[] args) {
        HelloJNI app = new HelloJNI();
        System.out.println(app.sayHello());
    }
}

创建 hello.c,实现与 Java 类名、方法名严格对应的 JNI 函数:

#include <jni.h>
#include <stdio.h>

JNIEXPORT jstring JNICALL
Java_HelloJNI_sayHello(JNIEnv *env, jobject obj) {
    return (*env)->NewStringUTF(env, "Hello from JNI!");
}
函数名必须为 Java_HelloJNI_sayHello,其中 HelloJNI 是类名,sayHello 是方法名。若包名存在,需用下划线连接,例如 Java_com_example_HelloJNI_sayHello。

编译与运行

使用 CMake 组织构建,创建 CMakeLists.txt:

cmake_minimum_required(VERSION 3.22)
project(hello)

add_library(hello SHARED hello.c)

target_include_directories(hello PRIVATE
    ${JAVA_HOME}/include
    ${JAVA_HOME}/include/linux
)

执行构建与运行:

# 编译 Java
javac HelloJNI.java

# 生成构建目录并编译动态库
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
cmake --build build

# 将动态库放到 Java 可加载的位置
cp build/libhello.so .

# 运行
java -Djava.library.path=. HelloJNI

预期输出:

Hello from JNI!

结果判读与常见问题

现象原因处理
UnsatisfiedLinkError: no hello in java.library.path动态库未生成或路径未指定确认 libhello.so 存在,并用 -Djava.library.path 指向其目录
UnsatisfiedLinkError: Java_HelloJNI_sayHello函数名拼写错误或未导出核对类名、方法名与 JNIEXPORT 声明
fatal error: jni.h: No such fileCMake 未包含 JDK 头文件路径检查 JAVA_HOME 并确认 include/linux 存在
clang: command not foundNDK 工具链未加入 PATH将 NDK 的 prebuilt/.../bin 加入 PATH
验证通过的标准:java HelloJNI 输出 Hello from JNI!,且过程中无 UnsatisfiedLinkError 或编译错误。此时 JDK、NDK、CMake 三者已形成可复用的 JNI 工具链。