C++ 怎么通过 JNI(Java Native Interface)实现 Android Java 层与底层 C++ 的高性能桥接

大宇酱_5465

大宇酱_5465

2026-10-21

334人浏览

原创

c++oding="utf-8" ?>

java调用c++必须通过jni函数签名,因jvm与c++内存隔离,类型无法自动转换;签名错误会导致unsatisfiedlinkerror;需严格匹配包名、类名、方法名及jni类型编码。

c++ 怎么通过 jni(java native interface)实现 android java 层与底层 c++ 的高性能桥接

Java 层无法直接调用 C++ 函数,必须通过 JNI 接口层中转;不写对 JNIEnv* 和线程绑定逻辑,调用会崩溃或返回乱码。

为什么 Java 调用 C++ 必须走 JNI 函数签名这道“关卡”

Java 虚拟机(ART)和 C++ 运行在完全隔离的内存空间里,java.lang.String 和 std::string 不能自动转换,int[] 和 int* 也不能直接传参。JNI 强制要求你显式声明类型映射规则——这就是函数签名(如 ([BI)I 表示输入 byte 数组和 int,返回 int)。漏写、写错签名,System.loadLibrary 不报错,但首次调用 native 方法时直接抛 UnsatisfiedLinkError。

常见错误现象:

  • Java 层调用 NativeUtils.calcDesMac64() 报 No implementation found for native Lcom/mypackage/jni/CalcMac;.Native_JniCalcDesMac64
  • C++ 函数名拼错,比如写成 Java_com_mypackage_jni_CalcMac_Native_JniCalcDesMac64 却漏了下划线或大小写
  • 没加 JNIEXPORT 和 JNICALL 宏,导致符号未导出,链接失败

实操建议:

Android Remote Control
Android Remote Control

远程控制 Android 设备,支持截图、自动化操作手机 App 等功能。本应用使用 uiautomator2 作为底层框架,通过 ADB 连接设备,用户无需 Root(1)查看手机屏幕;(2)自动化操作手机 App;(3)远程安装/卸载应用……

下载
  • 用 javah -jni(旧版)或 Android Studio 自动生成头文件,别手敲函数名
  • 签名必须严格匹配:包名用下划线替换点,类名和方法名之间用两个下划线,参数类型用 JNI 编码([B 是 byte[],I 是 int)
  • 函数声明必须带三个固定参数:JNIEnv* env、jobject obj(或 jclass cls 对应 static 方法)、再跟 Java 参数

如何安全地在 C++ 中访问 Java 传入的 byte[] 并避免内存越界

Java 的 byte[] 在 JNI 中不是裸指针,而是 jbyteArray 类型,必须用 env->GetByteArrayElements() 或更推荐的 env->GetByteArrayRegion() 拷贝出来——前者可能触发 pinning(锁定内存),后者只读拷贝更轻量,也避免 ART GC 移动数组时出问题。

容易踩的坑:

  • 直接强制转换 (jbyte*)array,运行时崩溃或读到脏数据
  • 用了 GetByteArrayElements() 却忘记调用 ReleaseByteArrayElements(),造成内存泄漏
  • 传入空数组或 null,没做 if (array == nullptr) 判断,直接解引用

实操建议:

  • 优先用 env->GetByteArrayRegion(array, 0, len, (jbyte*)buf) 把数据拷进预分配的 C++ 缓冲区
  • 如果必须原地操作(如加密 inplace),用 env->GetPrimitiveArrayCritical() + ReleasePrimitiveArrayCritical(),但要极短时间完成,否则阻塞 GC
  • 所有数组长度必须用 env->GetArrayLength(array) 获取,不要信 Java 层传来的 len 参数(可能被篡改)

Java 层加载 so 库失败的三种典型原因和验证方式

System.loadLibrary("calcmac") 看似简单,实际背后涉及 ABI 匹配、路径查找、依赖解析三重校验。任一环节失败都静默失败,只在调用 native 方法时报 UnsatisfiedLinkError。

常见错误现象:

  • App 在 arm64-v8a 设备上闪退,logcat 显示 dlopen failed: library "libcalcmac.so" not found
  • so 加载成功,但调用时报 java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "logd"
  • 模拟器上正常,真机上崩溃,因为 NDK 版本太新,targetSdkVersion 太低

实操建议:

  • 确认 so 文件放在 src/main/jniLibs/arm64-v8a/libcalcmac.so(不是 libs/),且 gradle 中没禁用对应 ABI:ndk { abiFilters 'arm64-v8a' }
  • 用 readelf -d libcalcmac.so | grep NEEDED 检查是否依赖了高版本 libc(如 libc++.so.21),换成 libc++_shared.so 并打包进 apk
  • 在 static{} 块里加 try-catch 包裹 loadLibrary,并打印 Log.e(TAG, "load failed", e),否则异常被吞掉

从 C++ 回调 Java 方法时 JNIEnv* 不能跨线程复用

Android 的 JNI 环境(JNIEnv*)是线程绑定的。C++ 在子线程(如 decode 线程、网络回调线程)里直接用主线程传来的 env 调用 env->CallVoidMethod(),大概率 crash 或静默失败。

根本原因:

  • JNIEnv* 实际是线程局部存储(TLS)里的一个指针,每个线程有自己的副本
  • 主线程的 env 在子线程里是无效地址,ART 可能返回 null 或任意值

实操建议:

  • 在 Java 层传入 jobject 时,用 env->NewGlobalRef(obj) 创建全局引用,防止 Java 对象被 GC
  • 在子线程开头调用 g_jvm->AttachCurrentThread(&env, nullptr) 获取当前线程的 JNIEnv*(g_jvm 是 JavaVM*,在 JNI_OnLoad 里保存)
  • 退出前务必调用 g_jvm->DetachCurrentThread(),否则线程退出后 JVM 不释放资源
  • 回调方法尽量用 env->CallStaticXXXMethod(),避免传 jobject 生命周期管理问题

最易被忽略的一点:JNIEnv* 不是“一次获取,到处可用”,它本质上是个临时句柄,生命周期严格绑定于当前线程上下文。任何跨线程传递或缓存 env 的做法,都是在埋雷。

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.15

10877

6

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

2023.07.05

8002

9

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

2023.07.31

6932

8

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.01

1164

3

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.02

948

3

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

1416

5

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

2869

5

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

2023.08.03

20151

3

配置java环境变量
配置java环境变量

配置Java环境变量是为了让操作系统能够识别和使用Java的相关命令和功能。本专题为大家提供配置java环境变量相关文章,帮助大家解决问题。

2023.08.03

1255

8

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
dev.java 官方:Learn Java
dev.java 官方:Learn Java

共0课时 | 0人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习