0赞
赞赏
更多好文
Android JNI 开发完全指南:从入门到精通
在 Android 开发中,有时我们不得不跳出 Java/Kotlin 的舒适区,深入到底层 C/C++ 世界。无论是为了极致性能、复用已有 C/C++ 库,还是调用系统级 API,JNI(Java Native Interface) 都是我们绕不开的桥梁。
本文将带你系统掌握 Android JNI 开发的核心知识,涵盖环境搭建、函数注册、数据类型转换、异常处理、线程安全等关键内容,助你从零构建稳定高效的 JNI 模块。
一、什么是 JNI?
JNI 是 Java 提供的一套标准接口,允许 Java 代码与本地(Native)语言(如 C/C++)进行交互。在 Android 中,它常用于:
- 调用高性能计算逻辑(如图像处理、音视频编解码)
- 复用已有的 C/C++ 库(如 OpenSSL、FFmpeg)
- 访问操作系统底层功能
- 实现反逆向保护(混淆关键逻辑)
⚠️ 注意:JNI 并非“银弹”。过度使用会增加复杂性、降低可维护性,并可能引入内存泄漏或崩溃风险。仅在必要时使用!
二、开发环境准备
1. 必备工具
- Android Studio(建议最新稳定版)
- NDK(Native Development Kit):通过 SDK Manager 安装
- CMake 或 ndk-build(推荐 CMake,官方主推)
2. 创建支持 JNI 的项目
在 build.gradle(Module 级别)中启用 NDK 支持:
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'armeabi-v7a' // 指定目标 ABI
}
}
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.18.1'
}
}
}
在 src/main/cpp/ 目录下创建 native-lib.cpp 和 CMakeLists.txt。
三、第一个 JNI 程序
1. Java 层声明 native 方法
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib"); // 加载 so 库
}
public native String getStringFromJNI();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = findViewById(R.id.sample_text);
tv.setText(getStringFromJNI());
}
}
2. C++ 实现
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapp_MainActivity_getStringFromJNI(JNIEnv *env, jobject thiz) {
std::string hello = "Hello from C++!";
return env->NewStringUTF(hello.c_str());
}
🔑 函数命名规则:
Java_包名_类名_方法名
使用extern "C"防止 C++ 名称修饰(name mangling)
3. CMakeLists.txt 配置
cmake_minimum_required(VERSION 3.18.1)
project("native-lib")
add_library(native-lib SHARED native-lib.cpp)
find_library(log-lib log)
target_link_libraries(native-lib ${log-lib})
编译后,APK 将包含对应 ABI 的 .so 文件。
四、JNI 函数注册方式
方式一:静态注册(默认)
即上述的命名约定方式。优点是简单,缺点是函数名冗长,且每次 Java 方法变动需重写 C++ 函数名。
方式二:动态注册(推荐用于大型项目)
在 JNI_OnLoad 中手动注册:
static JNINativeMethod methods[] = {
{"getStringFromJNI", "()Ljava/lang/String;", (void*)getStringFromJNI},
// 更多方法...
};
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
jclass clazz = env->FindClass("com/example/myapp/MainActivity");
env->RegisterNatives(clazz, methods, sizeof(methods)/sizeof(methods[0]));
return JNI_VERSION_1_6;
}
优势:
- 函数名自由命名
- 启动时一次性注册,性能略优
- 更易管理大量 native 方法
五、数据类型映射与转换
| Java 类型 | JNI 类型 | C/C++ 类型 |
|---|---|---|
| boolean | jboolean | unsigned char |
| byte | jbyte | signed char |
| char | jchar | unsigned short |
| short | jshort | short |
| int | jint | int |
| long | jlong | long long |
| float | jfloat | float |
| double | jdouble | double |
| Object | jobject | void* |
常见操作示例
字符串处理
// Java → C++
jstring jstr = ...;
const char* cstr = env->GetStringUTFChars(jstr, nullptr);
// 使用 cstr...
env->ReleaseStringUTFChars(jstr, cstr); // 必须释放!
// C++ → Java
const char* msg = "Hello";
jstring result = env->NewStringUTF(msg);
数组操作
jintArray arr = ...;
jsize len = env->GetArrayLength(arr);
jint* data = env->GetIntArrayElements(arr, nullptr);
// 修改 data...
env->ReleaseIntArrayElements(arr, data, 0); // 0 表示提交更改
🚨 警告:所有
GetXXX操作必须配对ReleaseXXX,否则会导致内存泄漏或 JVM 崩溃!
六、异常处理
JNI 中的异常不会自动抛给 Java 层,需手动检查:
jclass cls = env->FindClass("NonExistentClass");
if (cls == nullptr) {
// 发生异常,清除并返回
env->ExceptionClear();
return -1;
}
常用方法:
ExceptionCheck():检查是否有 pending 异常ExceptionOccurred():获取异常对象ExceptionClear():清除异常(否则后续 JNI 调用会失败)
七、线程与 JNIEnv
- JNIEnv 不是线程安全的! 每个线程有独立的 JNIEnv。
- 在非 Java 创建的线程中调用 JNI,需先附加到 VM:
JavaVM* g_jvm; // 全局保存,在 JNI_OnLoad 中获取
// 在子线程中
JNIEnv* env;
g_jvm->AttachCurrentThread(&env, nullptr);
// 使用 env...
g_jvm->DetachCurrentThread();
八、最佳实践与避坑指南
- 避免频繁跨语言调用:JNI 调用有开销,尽量批量处理数据。
- 及时释放引用:局部引用在函数返回后自动释放,但循环中需手动
DeleteLocalRef。 - 慎用全局引用:使用
NewGlobalRef后务必DeleteGlobalRef。 - ABI 兼容性:明确支持的 CPU 架构,避免“so not found”错误。
- 调试技巧:使用
__android_log_print打印日志(需链接 log 库)。
九、结语
JNI 是 Android 开发中的“双刃剑”——用得好,性能飞跃;用不好,bug 难测。掌握其核心机制、遵循规范、重视内存管理,才能安全高效地发挥其威力。
📚 推荐延伸阅读:
- Android NDK 官方文档
- 《Android 软件安全权威指南》—— JNI 安全章节
- JNI Specification(Oracle 官方)
如果你正在开发音视频、游戏引擎、加密模块等高性能组件,JNI 将是你不可或缺的利器。现在,就去动手试试吧!
欢迎关注本公众号,回复“JNI 示例”获取完整 Demo 工程源码!
如有疑问,欢迎留言讨论 👇
