Hello JNI:如何在Java中调用C代码?ndk-samples入门样本完整教程

Hello JNI:如何在Java中调用C代码?ndk-samples入门样本完整教程

【免费下载链接】ndk-samples Android NDK samples with Android Studio 【免费下载链接】ndk-samples 项目地址: https://gitcode.com/gh_mirrors/nd/ndk-samples

ndk-samples 是 Android 官方的 NDK 示例仓库,其中 Hello JNI 是最经典的入门样本:它用最少的代码演示了如何在 Java(Kotlin)中调用 C 代码——在界面上显示一行由 C 函数返回的字符串。如果你想学 JNI 编程、了解 Android NDK 的工作方式,这个不到 30 行 C 代码的样本就是最好的起点。

一、什么是 JNI?为什么要在 Java 里调 C?

JNI(Java Native Interface,Java 本地接口) 是 Android/Java 生态中连接托管代码(Java/Kotlin)与本地代码(C/C++)的桥梁。

📌 在以下场景中,你会真正需要它:

  • 复用已有的 C/C++ 算法库(图像处理、音频、密码学等)
  • 对性能敏感的核心逻辑提速
  • 调用 Android NDK 提供的系统 API(相机、传感器图、Neural Networks API 等)

工作流程一句话概括:Java 声明 external 方法 → C 按约定命名实现同名函数 → 运行时通过 JNI 找到并调用 C 函数。

二、快速上手:克隆仓库并运行 Hello JNI 样本

只需 4 步即可看到运行效果:

1️⃣ 克隆仓库:

git clone https://gitcode.com/gh_mirrors/nd/ndk-samples

2️⃣ 用 Android Studio 4.2+ 打开整个项目(需安装 NDK 组件) 3️⃣ 在顶部工具栏选择 hello-jni 样本(必要时先 Sync Gradle) 4️⃣ 点击运行按钮,设备屏幕上将显示 C 代码返回的文本

💡 官方说明:hello-jni/README.md 中给出了同样的 Getting Started 步骤;仓库整体结构可参考 ARCHITECTURE.md。

三、源码拆解:3 个文件看懂"Java 调 C"全过程

整个样本的核心代码只有 3 个文件,我们逐一拆解。

3.1 Kotlin 侧:声明 external 方法并加载 so 库

在 HelloJni.kt 中做了两件事:

external fun stringFromJNI(): String?   // 声明本地方法,不写实现

companion object {
    init { System.loadLibrary("hello-jni") }  // 加载 C 编译出的动态库
}
  • external 关键字表示"这个方法在别处(C 库里)实现",JVM 会在运行时按命名规则自动匹配 C 函数
  • System.loadLibrary("hello-jni") 加载安装时由包管理器解压到应用私有目录的 libhello-jni.so

然后在 onCreate 中直接调用并显示结果:

binding.helloTextview.text = stringFromJNI()

3.2 C 侧:一个约定命名的函数就是 JNI 的全部

C 实现只有几行,见 hello-jni.cpp:

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env, jobject) {
  std::string hello = "Hello from JNI.";
  return env->NewStringUTF(hello.c_str());
}

⚠️ 函数名不是随便起的,它由 4 段拼接而成,这就是 JNI 的核心规则:

片段含义
Java_JNI 固定前缀
com_example_hellojniKotlin/Java 包名(. 换成 _)
HelloJni类名
stringFromJNI方法名

只要包名、类名、方法名任一变一个字符,JVM 就找不到实现,调用时会抛出 UnsatisfiedLinkError——样本里特意留了一个未实现的 unimplementedStringFromJNI() 方法(见 HelloJni.kt)来演示这个"报错"行为。

另外两个细节值得注意:

  • JNIEnv* env 是指向当前线程 JNI 环境的指针,C 侧靠它创建返回给 Java 的字符串(NewStringUTF)
  • jstring、jobject 都是 JNI 对 Java 类型的"镜像",jstring ≈ String,jobject ≈ 任意 Java 对象

3.3 构建侧:CMake 把 C 编译成 .so

CMakeLists.txt 定义了动态库与链接依赖:

add_library(hello-jni SHARED hello-jni.cpp)
target_link_libraries(hello-jni android log)

而 Kotlin 侧能找到这个 CMakeLists.txt,靠的是 build.gradle 中的一行配置:

externalNativeBuild {
    cmake { path "src/main/cpp/CMakeLists.txt" }
}

也就是说:Gradle 负责整体打包,CMake 负责编译 C 代码,两者在 build.gradle 里会师。这就是现代 Android 原生开发的标准构建链路。

四、界面与其余文件速览

运行效果就是页面中央一行文字——来自 C 的 Hello from JNI.:

ndk-samples 中 san-angeles 样本的运行截图:一个完全由 OpenGL ES 本地代码渲染的 3D 场景,展示 Android NDK 原生编程的视觉效果

五、学会 Hello JNI 之后:还能做点什么?

Hello JNI 只是"握手"级别的演示,真正上手 NDK 后,你能做出性能敏感的完整原生应用。仓库里的游戏样本全部用本地代码直接渲染画面,不依赖 Android 视图系统:

ndk-samples 中 endless-tunnel 样本的运行截图:用本地 C++ 与 OpenGL 实现的 3D 隧道闯关小游戏,左上角是实时分数

这些样本的共同点:C/C++ 完成重活,Java/Kotlin 只负责薄薄的壳。

其他推荐的进阶入门样本

样本模块路径演示内容
Hello CMakehello-cmake 见仓库目录CMake 构建链(与 Hello JNI 配套)
Native Activitynative-activity/无 Java 壳的纯原生 Activity
Hello Oboehello-oboe/用 NDK 音频 API 播放声音
Hello Vulkanhello-vulkan/Vulkan 图形渲染入门
Endless Tunnelendless-tunnel/完整的游戏引擎(OpenGL ES)

📚 更多样本清单与仓库目录结构,参见仓库根目录的 README.md 与 REFERENCE.md。

六、常见问题(FAQ)

Q1:为什么调用 external 方法时崩溃,提示 UnsatisfiedLinkError? 九成是 C 函数名与"包名_类名_方法名"约定不匹配,或者 so 库没被 System.loadLibrary 加载。

Q2:Kotlin 里用 external 还是 Java 的 native? 完全可以。两者对 JNI 而言是等价的,Hello JNI 样本现在用 Kotlin 声明(external fun)。

Q3:C 里能传复杂对象给 Java 吗? 可以,但建议优先用基础类型、字符串、字节数组。JNI 对对象的操作较繁琐,实践中常见做法是 C 侧用 POD 结构体/字节缓冲,Java 侧用 ByteBuffer 或 byte[] 交接数据。

Q4:这个样本能直接当生产项目模板吗? 官方明确提醒:样本以"简洁演示 API"为目标,并非生产级项目。做正式项目建议用 Android Studio 的 "Native C++" 新工程模板,把本仓库当参考。

七、写在最后

回顾一下 Hello JNI 教会我们的最小知识闭环:

  1. Java/Kotlin:external 声明方法 + System.loadLibrary 加载 so 库
  2. C/C++:按 Java_包名_类名_方法名 实现函数,通过 JNIEnv* 与 Java 世界交互
  3. 构建:CMake 编译 C 代码,Gradle 通过 externalNativeBuild 接管整条流水线

吃透这 30 行 C 代码,你就拿到了打开 Android 本地世界大门的钥匙 🗝️。接下来可以顺着 native-activity、hello-oboe 等样本继续深入,逐步掌握相机、音频、图形、神经网络等 NDK API。

【免费下载链接】ndk-samples Android NDK samples with Android Studio 【免费下载链接】ndk-samples 项目地址: https://gitcode.com/gh_mirrors/nd/ndk-samples

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

抵扣说明:

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

余额充值