🏗️ JNI原理与使用深度解析
一、JNI基础概念与架构
1.1 JNI是什么?
JNI(Java Native Interface) 是Java平台提供的一个编程框架,允许:
- Java代码调用Native代码(C/C++)
- Native代码调用Java代码
- 在Java和Native之间共享数据和对象
1.2 JNI在Android中的位置
scss
Java层 (Kotlin/Java)
↓ JNI桥接
JNI层 (C/C++)
↓
Native层 (系统库、硬件驱动)
1.3 数据类型映射
| Java类型 | JNI类型 | C/C++类型 | 说明 |
|---|---|---|---|
| boolean | jboolean | unsigned char | 8位 |
| byte | jbyte | signed char | 8位 |
| char | jchar | unsigned short | 16位 |
| short | jshort | short | 16位 |
| int | jint | int | 32位 |
| long | jlong | long long | 64位 |
| float | jfloat | float | 32位 |
| double | jdouble | double | 64位 |
| void | void | void | - |
| Object | jobject | _jobject* | 对象引用 |
| String | jstring | _jstring* | 字符串引用 |
| Class | jclass | _jclass* | 类引用 |
| Object[] | jobjectArray | _jobjectArray* | 对象数组 |
| boolean[] | jbooleanArray | _jbooleanArray* | 布尔数组 |
| ... | ... | ... | ... |
二、JNI开发环境搭建
2.1 Android Studio配置
gradle
// app/build.gradle
android {
defaultConfig {
externalNativeBuild {
cmake {
cppFlags "-std=c++17 -frtti -fexceptions"
arguments "-DANDROID_STL=c++_shared"
}
}
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.22.1"
}
}
// 打包时包含.so文件
sourceSets {
main {
jniLibs.srcDirs = ['src/main/cpp/libs']
}
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.12.0'
}
2.2 CMakeLists.txt配置
cmake
# CMakeLists.txt
cmake_minimum_required(VERSION 3.18.1)
project("native-lib")
# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 添加NDK头文件路径
include_directories(${CMAKE_SOURCE_DIR}/src/main/cpp/include)
include_directories(${ANDROID_NDK}/sources/android/native_app_glue)
# 添加预构建库
add_library(log-lib STATIC IMPORTED)
set_target_properties(log-lib PROPERTIES IMPORTED_LOCATION
${ANDROID_NDK}/platforms/android-${ANDROID_PLATFORM}/arch-arm/usr/lib/liblog.so)
# 添加自己的库
add_library(
native-lib # 库名
SHARED # 共享库
src/main/cpp/native-lib.cpp # 源文件
)
# 链接库
target_link_libraries(
native-lib # 目标库
android # Android特定API
log-lib # Logcat日志
${log-lib}
)
2.3 JNI函数命名规范
cpp
// 静态注册命名规则:Java_包名_类名_方法名
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_myapp_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
// 参数说明:
// JNIEnv* env: JNI环境指针,提供所有JNI函数
// jobject thiz: 调用该native方法的Java对象(this)
// 后续参数: Java方法的参数
三、静态注册 vs 动态注册
3.1 静态注册(传统方式)
java
// Java层
public class NativeLib {
static {
System.loadLibrary("native-lib");
}
public native String getMessage();
public native int calculate(int a, int b);
public native void processArray(int[] array);
}
cpp
// Native层 - 静态注册
#include <jni.h>
#include <string>
// 必须按照规则命名
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_NativeLib_getMessage(JNIEnv* env, jobject thiz) {
return env->NewStringUTF("Hello from static registration");
}
extern "C" JNIEXPORT jint JNICALL
Java_com_example_NativeLib_calculate(JNIEnv* env, jobject thiz, jint a, jint b) {
return a + b;
}
extern "C" JNIEXPORT void JNICALL
Java_com_example_NativeLib_processArray(
JNIEnv* env, jobject thiz, jintArray array) {
// 处理数组
}
3.2 动态注册(推荐方式)
cpp
// Native层 - 动态注册
#include <jni.h>
#include <string>
#include <android/log.h>
#define LOG_TAG "JNI_DEMO"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
// Native方法实现
jstring native_getMessage(JNIEnv* env, jobject thiz) {
LOGD("native_getMessage called");
return env->NewStringUTF("Hello from dynamic registration");
}
jint native_calculate(JNIEnv* env, jobject thiz, jint a, jint b) {
return a * b;
}
void native_processArray(JNIEnv* env, jobject thiz, jintArray array) {
jint* elements = env->GetIntArrayElements(array, nullptr);
jsize length = env->GetArrayLength(array);
for (int i = 0; i < length; i++) {
elements[i] *= 2;
}
env->ReleaseIntArrayElements(array, elements, 0);
}
// 方法映射表
static JNINativeMethod nativeMethods[] = {
{"getMessage", "()Ljava/lang/String;", (void*)native_getMessage},
{"calculate", "(II)I", (void*)native_calculate},
{"processArray", "([I)V", (void*)native_processArray}
};
// JNI_OnLoad - 库加载时自动调用
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
LOGD("JNI_OnLoad called");
JNIEnv* env = nullptr;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
LOGE("Failed to get JNIEnv");
return JNI_ERR;
}
// 找到Java类
jclass clazz = env->FindClass("com/example/NativeLib");
if (clazz == nullptr) {
LOGE("Failed to find class");
return JNI_ERR;
}
// 注册Native方法
jint result = env->RegisterNatives(
clazz,
nativeMethods,
sizeof(nativeMethods) / sizeof(JNINativeMethod)
);
if (result != JNI_OK) {
LOGE("Failed to register native methods");
return JNI_ERR;
}
LOGD("Native methods registered successfully");
return JNI_VERSION_1_6;
}
// JNI_OnUnload - 库卸载时调用
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) {
LOGD("JNI_OnUnload called");
}
3.3 方法签名详解
cpp
// 方法签名格式:(参数类型)返回值类型
// 基本类型签名
"()V" // void func()
"(I)V" // void func(int)
"(II)I" // int func(int, int)
"(Ljava/lang/String;)V" // void func(String)
"(I[Ljava/lang/String;)Z" // boolean func(int, String[])
// 类型签名速查表:
// Z -> boolean
// B -> byte
// C -> char
// S -> short
// I -> int
// J -> long
// F -> float
// D -> double
// V -> void
// L完整类名; -> 对象类型(如 Ljava/lang/String;)
// [类型 -> 数组(如 [I -> int[],[Ljava/lang/String; -> String[])
// 完整的类描述符
"(Ljava/lang/String;ILjava/util/List;)V"
// 对应:void func(String, int, List)
四、JNI核心API详解
4.1 字符串操作
cpp
// 字符串转换示例
void stringOperations(JNIEnv* env) {
// 1. Java字符串 -> C字符串
jstring javaStr = env->NewStringUTF("Hello Java");
// 转换为UTF-8字符串(需要释放)
const char* cStr = env->GetStringUTFChars(javaStr, nullptr);
if (cStr != nullptr) {
printf("C String: %s\n", cStr);
env->ReleaseStringUTFChars(javaStr, cStr);
}
// 转换为UTF-16字符串(用于宽字符)
const jchar* wideStr = env->GetStringChars(javaStr, nullptr);
if (wideStr != nullptr) {
// 处理宽字符串
env->ReleaseStringChars(javaStr, wideStr);
}
// 2. C字符串 -> Java字符串
const char* cppStr = "Hello from C++";
jstring newJavaStr = env->NewStringUTF(cppStr);
// 3. 获取字符串长度
jsize utf8Length = env->GetStringUTFLength(javaStr);
jsize unicodeLength = env->GetStringLength(javaStr);
// 4. 获取字符串区域(避免完整复制)
char buffer[256];
env->GetStringUTFRegion(javaStr, 0, 5, buffer);
buffer[5] = '\0';
// 5. 预分配字符串
jcharArray charArray = env->NewCharArray(100);
// ... 填充charArray
jstring fromCharArray = env->NewString(env->GetCharArrayElements(charArray, nullptr), 100);
}
// 字符串操作最佳实践
class JNIStringGuard {
private:
JNIEnv* env;
jstring javaString;
const char* cString;
public:
JNIStringGuard(JNIEnv* env, jstring str) : env(env), javaString(str) {
cString = env->GetStringUTFChars(javaString, nullptr);
}
~JNIStringGuard() {
if (cString) {
env->ReleaseStringUTFChars(javaString, cString);
}
}
const char* get() const { return cString; }
operator const char*() const { return cString; }
};
4.2 数组操作
cpp
// 基本类型数组
void processIntArray(JNIEnv* env, jintArray array) {
// 方式1:获取数组指针(可修改原始数组)
jint* elements = env->GetIntArrayElements(array, nullptr);
jsize length = env->GetArrayLength(array);
for (int i = 0; i < length; i++) {
elements[i] *= 2;
}
// 第三个参数:
// 0 - 复制回Java数组并释放C数组
// JNI_COMMIT - 复制回Java数组但不释放C数组
// JNI_ABORT - 不复制回Java数组,直接释放C数组
env->ReleaseIntArrayElements(array, elements, 0);
// 方式2:获取数组区域(适合读取,不修改)
jint buffer[100];
env->GetIntArrayRegion(array, 0, 100, buffer);
// 方式3:设置数组区域
jint newData[100];
// ... 填充newData
env->SetIntArrayRegion(array, 0, 100, newData);
}
// 对象数组
void processObjectArray(JNIEnv* env, jobjectArray array) {
jsize length = env->GetArrayLength(array);
for (int i = 0; i < length; i++) {
jobject element = env->GetObjectArrayElement(array, i);
if (element != nullptr) {
// 处理每个对象
jclass stringClass = env->FindClass("java/lang/String");
jmethodID toUpperCase = env->GetMethodID(stringClass, "toUpperCase", "()Ljava/lang/String;");
jobject upper = env->CallObjectMethod(element, toUpperCase);
env->SetObjectArrayElement(array, i, upper);
}
}
}
// 创建新数组
jintArray createIntArray(JNIEnv* env, jsize size) {
jintArray array = env->NewIntArray(size);
if (array != nullptr) {
jint* elements = env->GetIntArrayElements(array, nullptr);
for (int i = 0; i < size; i++) {
elements[i] = i * i;
}
env->ReleaseIntArrayElements(array, elements, 0);
}
return array;
}
// 多维数组
jobjectArray create2DArray(JNIEnv* env, jsize rows, jsize cols) {
// 找到内部数组的类
jclass intArrayClass = env->FindClass("[I");
// 创建外层数组
jobjectArray result = env->NewObjectArray(rows, intArrayClass, nullptr);
for (int i = 0; i < rows; i++) {
jintArray row = env->NewIntArray(cols);
jint* elements = env->GetIntArrayElements(row, nullptr);
for (int j = 0; j < cols; j++) {
elements[j] = i * cols + j;
}
env->ReleaseIntArrayElements(row, elements, 0);
env->SetObjectArrayElement(result, i, row);
env->DeleteLocalRef(row);
}
return result;
}
4.3 对象操作与反射
cpp
class JNIReflectionHelper {
private:
JNIEnv* env;
public:
explicit JNIReflectionHelper(JNIEnv* env) : env(env) {}
// 创建Java对象
jobject createObject(const char* className, const char* constructorSig, ...) {
jclass clazz = env->FindClass(className);
if (clazz == nullptr) return nullptr;
jmethodID constructor = env->GetMethodID(clazz, "<init>", constructorSig);
if (constructor == nullptr) return nullptr;
va_list args;
va_start(args, constructorSig);
jobject obj = env->NewObjectV(clazz, constructor, args);
va_end(args);
env->DeleteLocalRef(clazz);
return obj;
}
// 调用实例方法
template<typename T>
T callMethod(jobject obj, const char* methodName, const char* signature, ...) {
jclass clazz = env->GetObjectClass(obj);
jmethodID method = env->GetMethodID(clazz, methodName, signature);
va_list args;
va_start(args, signature);
T result;
if constexpr (std::is_same_v<T, jobject>) {
result = env->CallObjectMethodV(obj, method, args);
} else if constexpr (std::is_same_v<T, jint>) {
result = env->CallIntMethodV(obj, method, args);
} else if constexpr (std::is_same_v<T, jlong>) {
result = env->CallLongMethodV(obj, method, args);
} else if constexpr (std::is_same_v<T, jfloat>) {
result = env->CallFloatMethodV(obj, method, args);
} else if constexpr (std::is_same_v<T, jdouble>) {
result = env->CallDoubleMethodV(obj, method, args);
} else if constexpr (std::is_same_v<T, jboolean>) {
result = env->CallBooleanMethodV(obj, method, args);
} else {
env->CallVoidMethodV(obj, method, args);
}
va_end(args);
env->DeleteLocalRef(clazz);
return result;
}
// 访问字段
template<typename T>
T getField(jobject obj, const char* fieldName, const char* signature) {
jclass clazz = env->GetObjectClass(obj);
jfieldID field = env->GetFieldID(clazz, fieldName, signature);
T result;
if constexpr (std::is_same_v<T, jobject>) {
result = env->GetObjectField(obj, field);
} else if constexpr (std::is_same_v<T, jint>) {
result = env->GetIntField(obj, field);
} else if constexpr (std::is_same_v<T, jlong>) {
result = env->GetLongField(obj, field);
} // 其他类型类似...
env->DeleteLocalRef(clazz);
return result;
}
// 调用静态方法
jobject callStaticMethod(const char* className, const char* methodName,
const char* signature, ...) {
jclass clazz = env->FindClass(className);
if (clazz == nullptr) return nullptr;
jmethodID method = env->GetStaticMethodID(clazz, methodName, signature);
if (method == nullptr) {
env->DeleteLocalRef(clazz);
return nullptr;
}
va_list args;
va_start(args, signature);
jobject result = env->CallStaticObjectMethodV(clazz, method, args);
va_end(args);
env->DeleteLocalRef(clazz);
return result;
}
};
// 使用示例
void reflectionExample(JNIEnv* env, jobject context) {
JNIReflectionHelper helper(env);
// 创建Java对象
jobject point = helper.createObject(
"android/graphics/Point",
"(II)V",
100, 200
);
// 调用方法
jint x = helper.callMethod<jint>(point, "getX", "()I");
jint y = helper.callMethod<jint>(point, "getY", "()I");
// 调用静态方法
jobject display = helper.callStaticMethod(
"android/view/Display",
"getDefaultDisplay",
"(Landroid/content/Context;)Landroid/view/Display;",
context
);
// 访问字段
jobject size = helper.getField<jobject>(display, "mSize", "Landroid/graphics/Point;");
}
五、引用管理
5.1 局部引用 vs 全局引用
cpp
void referenceManagement(JNIEnv* env) {
// 1. 局部引用(Local Reference)
jstring localStr = env->NewStringUTF("局部引用");
// 在函数返回时自动释放,或手动释放:
env->DeleteLocalRef(localStr);
// 重要:局部引用数量有限制(默认16个)
for (int i = 0; i < 100; i++) {
jstring temp = env->NewStringUTF("test");
env->DeleteLocalRef(temp); // 必须及时释放
}
// 2. 全局引用(Global Reference)
jstring globalStr = static_cast<jstring>(env->NewGlobalRef(localStr));
// 全局引用不会被自动释放,必须手动:
env->DeleteGlobalRef(globalStr);
// 3. 弱全局引用(Weak Global Reference)
jstring weakStr = static_cast<jstring>(env->NewWeakGlobalRef(localStr));
// 检查弱引用是否有效
if (env->IsSameObject(weakStr, nullptr) == JNI_FALSE) {
// 引用仍然有效
}
env->DeleteWeakGlobalRef(weakStr);
}
// 引用管理工具类
template<typename T>
class JNIGlobalRef {
private:
T ref;
public:
explicit JNIGlobalRef(JNIEnv* env, T obj) : ref(nullptr) {
if (obj != nullptr) {
ref = static_cast<T>(env->NewGlobalRef(obj));
}
}
~JNIGlobalRef() {
if (ref != nullptr) {
JNIEnv* env = getEnv();
if (env != nullptr) {
env->DeleteGlobalRef(ref);
}
}
}
// 禁止拷贝
JNIGlobalRef(const JNIGlobalRef&) = delete;
JNIGlobalRef& operator=(const JNIGlobalRef&) = delete;
// 允许移动
JNIGlobalRef(JNIGlobalRef&& other) noexcept : ref(other.ref) {
other.ref = nullptr;
}
JNIGlobalRef& operator=(JNIGlobalRef&& other) noexcept {
if (this != &other) {
if (ref != nullptr) {
JNIEnv* env = getEnv();
if (env != nullptr) {
env->DeleteGlobalRef(ref);
}
}
ref = other.ref;
other.ref = nullptr;
}
return *this;
}
T get() const { return ref; }
operator T() const { return ref; }
private:
static JNIEnv* getEnv() {
JNIEnv* env = nullptr;
JavaVM* vm = getJavaVM();
if (vm != nullptr) {
vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
}
return env;
}
static JavaVM* getJavaVM() {
static JavaVM* vm = nullptr;
// 在JNI_OnLoad中设置
return vm;
}
};
5.2 线程中的JNI使用
cpp
// Native线程访问JVM
class NativeThread {
private:
JavaVM* javaVM;
JNIGlobalRef<jobject> javaObject;
public:
NativeThread(JavaVM* vm, jobject obj) : javaVM(vm), javaObject(nullptr) {
JNIEnv* env = nullptr;
vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
if (env != nullptr) {
javaObject = JNIGlobalRef<jobject>(env, obj);
}
}
void runInNativeThread() {
// 在Native线程中访问Java对象
JNIEnv* env = attachThread();
if (env != nullptr) {
// 可以安全地调用Java方法
jclass clazz = env->GetObjectClass(javaObject.get());
jmethodID method = env->GetMethodID(clazz, "onNativeCallback", "(I)V");
env->CallVoidMethod(javaObject.get(), method, 123);
detachThread();
}
}
private:
JNIEnv* attachThread() {
JNIEnv* env = nullptr;
javaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
if (env == nullptr) {
// 当前线程未附加到JVM,需要附加
JavaVMAttachArgs args = {JNI_VERSION_1_6, "NativeThread", nullptr};
if (javaVM->AttachCurrentThread(&env, &args) != JNI_OK) {
return nullptr;
}
}
return env;
}
void detachThread() {
// 检查是否需要分离
JNIEnv* env = nullptr;
if (javaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) == JNI_EDETACHED) {
javaVM->DetachCurrentThread();
}
}
};
六、JNI最佳实践
6.1 性能优化
cpp
// 1. 缓存ID和类引用
class CachedJNI {
private:
struct CachedIDs {
jclass stringClass;
jmethodID toUpperCase;
jfieldID valueField;
CachedIDs(JNIEnv* env) {
stringClass = static_cast<jclass>(env->NewGlobalRef(
env->FindClass("java/lang/String")));
toUpperCase = env->GetMethodID(stringClass, "toUpperCase",
"()Ljava/lang/String;");
valueField = env->GetFieldID(stringClass, "value", "[C");
}
~CachedIDs() {
JNIEnv* env = getEnv();
if (env != nullptr) {
env->DeleteGlobalRef(stringClass);
}
}
};
static CachedIDs& getCached() {
static CachedIDs cached(getEnv());
return cached;
}
};
// 2. 减少JNI调用次数
jstring efficientStringProcessing(JNIEnv* env, jobjectArray stringArray) {
jsize count = env->GetArrayLength(stringArray);
std::vector<std::string> nativeStrings;
nativeStrings.reserve(count);
// 一次性获取所有字符串
for (int i = 0; i < count; i++) {
jstring str = static_cast<jstring>(env->GetObjectArrayElement(stringArray, i));
const char* cstr = env->GetStringUTFChars(str, nullptr);
nativeStrings.emplace_back(cstr);
env->ReleaseStringUTFChars(str, cstr);
env->DeleteLocalRef(str);
}
// 在Native层进行批量处理
std::string result;
for (const auto& str : nativeStrings) {
result += str + " ";
}
return env->NewStringUTF(result.c_str());
}
// 3. 使用直接缓冲区(Direct Buffer)
void processDirectBuffer(JNIEnv* env, jobject directBuffer) {
void* buffer = env->GetDirectBufferAddress(directBuffer);
jlong capacity = env->GetDirectBufferCapacity(directBuffer);
if (buffer != nullptr) {
// 直接操作内存,避免拷贝
auto* data = static_cast<float*>(buffer);
for (int i = 0; i < capacity / sizeof(float); i++) {
data[i] *= 2.0f;
}
}
}
6.2 内存管理
cpp
// 自动资源管理RAII类
class JNIEnvPtr {
private:
JavaVM* vm;
JNIEnv* env;
bool attached;
public:
explicit JNIEnvPtr(JavaVM* vm) : vm(vm), env(nullptr), attached(false) {
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) == JNI_EDETACHED) {
vm->AttachCurrentThread(&env, nullptr);
attached = true;
}
}
~JNIEnvPtr() {
if (attached) {
vm->DetachCurrentThread();
}
}
JNIEnv* operator->() const { return env; }
JNIEnv* get() const { return env; }
// 禁止拷贝
JNIEnvPtr(const JNIEnvPtr&) = delete;
JNIEnvPtr& operator=(const JNIEnvPtr&) = delete;
// 允许移动
JNIEnvPtr(JNIEnvPtr&& other) noexcept
: vm(other.vm), env(other.env), attached(other.attached) {
other.env = nullptr;
other.attached = false;
}
};
// 使用示例
void safeNativeFunction(JavaVM* vm) {
JNIEnvPtr env(vm);
// 现在可以安全地使用env
jstring result = env->NewStringUTF("Safe JNI usage");
// ...
}
6.3 异常处理
cpp
class JNIExceptionGuard {
private:
JNIEnv* env;
public:
explicit JNIExceptionGuard(JNIEnv* env) : env(env) {}
~JNIExceptionGuard() {
if (env->ExceptionCheck()) {
env->ExceptionDescribe(); // 打印异常信息到logcat
env->ExceptionClear(); // 清除异常
}
}
bool checkException() const {
return env->ExceptionCheck() != JNI_FALSE;
}
void throwNewException(const char* className, const char* message) {
jclass clazz = env->FindClass(className);
if (clazz != nullptr) {
env->ThrowNew(clazz, message);
env->DeleteLocalRef(clazz);
}
}
};
// 使用示例
jstring safeNativeCall(JNIEnv* env) {
JNIExceptionGuard guard(env);
// 可能会抛出异常的操作
jclass clazz = env->FindClass("java/lang/String");
if (clazz == nullptr) {
guard.throwNewException("java/lang/RuntimeException", "Class not found");
return nullptr;
}
// 其他操作...
return env->NewStringUTF("Success");
}
七、JNI与Android Framework集成
7.1 Bitmap处理
cpp
#include <android/bitmap.h>
jobject processBitmap(JNIEnv* env, jobject bitmap) {
AndroidBitmapInfo info;
if (AndroidBitmap_getInfo(env, bitmap, &info) != ANDROID_BITMAP_RESULT_SUCCESS) {
return nullptr;
}
void* pixels;
if (AndroidBitmap_lockPixels(env, bitmap, &pixels) != ANDROID_BITMAP_RESULT_SUCCESS) {
return nullptr;
}
// 处理像素数据
if (info.format == ANDROID_BITMAP_FORMAT_RGBA_8888) {
auto* pixelData = static_cast<uint32_t*>(pixels);
for (int y = 0; y < info.height; y++) {
for (int x = 0; x < info.width; x++) {
uint32_t pixel = pixelData[y * info.width + x];
// 例如:将红色通道设为最大值
pixel = (pixel & 0xFF00FFFF) | 0x00FF0000;
pixelData[y * info.width + x] = pixel;
}
}
}
AndroidBitmap_unlockPixels(env, bitmap);
return bitmap;
}
7.2 与Java对象交互
cpp
// 创建复杂的Java对象
jobject createComplexObject(JNIEnv* env) {
// 创建HashMap
jclass hashMapClass = env->FindClass("java/util/HashMap");
jmethodID hashMapConstructor = env->GetMethodID(hashMapClass, "<init>", "()V");
jobject hashMap = env->NewObject(hashMapClass, hashMapConstructor);
// 获取put方法
jmethodID putMethod = env->GetMethodID(hashMapClass, "put",
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
// 添加键值对
jstring key1 = env->NewStringUTF("name");
jstring value1 = env->NewStringUTF("张三");
env->CallObjectMethod(hashMap, putMethod, key1, value1);
jstring key2 = env->NewStringUTF("age");
jobject value2 = env->NewObject(
env->FindClass("java/lang/Integer"),
env->GetMethodID(env->FindClass("java/lang/Integer"), "<init>", "(I)V"),
25
);
env->CallObjectMethod(hashMap, putMethod, key2, value2);
// 清理局部引用
env->DeleteLocalRef(key1);
env->DeleteLocalRef(value1);
env->DeleteLocalRef(key2);
env->DeleteLocalRef(value2);
env->DeleteLocalRef(hashMapClass);
return hashMap;
}
7.3 在Framework中的应用示例
cpp
// 模拟Android Framework中的JNI使用
class NativeHandler {
private:
JavaVM* javaVM;
jclass handlerClass;
jmethodID callbackMethod;
public:
NativeHandler() : javaVM(nullptr), handlerClass(nullptr),
callbackMethod(nullptr) {}
bool init(JavaVM* vm) {
javaVM = vm;
JNIEnv* env = getEnv();
if (env == nullptr) return false;
// 缓存Java类和方法ID
handlerClass = static_cast<jclass>(env->NewGlobalRef(
env->FindClass("android/os/Handler")));
callbackMethod = env->GetMethodID(handlerClass, "handleMessage",
"(Landroid/os/Message;)V");
return handlerClass != nullptr && callbackMethod != nullptr;
}
void sendMessageToJava(jobject handler, int what, jobject obj) {
JNIEnv* env = getEnv();
if (env == nullptr || handler == nullptr) return;
// 创建Message对象
jclass messageClass = env->FindClass("android/os/Message");
jmethodID obtainMethod = env->GetStaticMethodID(messageClass, "obtain",
"()Landroid/os/Message;");
jobject message = env->CallStaticObjectMethod(messageClass, obtainMethod);
// 设置Message内容
jfieldID whatField = env->GetFieldID(messageClass, "what", "I");
jfieldID objField = env->GetFieldID(messageClass, "obj", "Ljava/lang/Object;");
env->SetIntField(message, whatField, what);
env->SetObjectField(message, objField, obj);
// 发送给Handler
env->CallVoidMethod(handler, callbackMethod, message);
// 清理
env->DeleteLocalRef(messageClass);
env->DeleteLocalRef(message);
}
private:
JNIEnv* getEnv() {
JNIEnv* env = nullptr;
if (javaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return nullptr;
}
return env;
}
};
八、调试与性能分析
8.1 JNI调试技巧
cpp
// 1. 启用CheckJNI
// 在adb shell中执行:
// setprop debug.checkjni 1
// 或adb shell setprop dalvik.vm.checkjni true
// 2. 使用__android_log_print
#include <android/log.h>
#define TAG "JNI_DEBUG"
void debugExample(JNIEnv* env) {
__android_log_print(ANDROID_LOG_VERBOSE, TAG, "进入JNI函数");
// 检查JNI调用错误
if (env->ExceptionCheck()) {
__android_log_print(ANDROID_LOG_ERROR, TAG, "JNI调用抛出异常");
env->ExceptionDescribe();
env->ExceptionClear();
}
__android_log_print(ANDROID_LOG_DEBUG, TAG, "退出JNI函数");
}
// 3. 使用jni.h中的调试函数
void jniDebugFunctions() {
// 这些函数需要在CheckJNI启用时才有用
// env->GetStringUTFChars(nullptr, nullptr); // 会触发CheckJNI警告
}
// 4. 使用addr2line解析崩溃堆栈
// arm-linux-androideabi-addr2line -e libnative-lib.so -f -C 0x12345678
8.2 性能测试
cpp
#include <chrono>
class JNIPerformanceTest {
public:
static void testStringConversion(JNIEnv* env, jstring javaString, int iterations) {
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < iterations; i++) {
const char* cstr = env->GetStringUTFChars(javaString, nullptr);
// 模拟处理
size_t len = strlen(cstr);
env->ReleaseStringUTFChars(javaString, cstr);
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
__android_log_print(ANDROID_LOG_INFO, "PERF_TEST",
"String转换 %d次耗时: %lld微秒",
iterations, duration.count());
}
static void testArrayAccess(JNIEnv* env, jintArray array, int iterations) {
jsize length = env->GetArrayLength(array);
auto start = std::chrono::high_resolution_clock::now();
for (int iter = 0; iter < iterations; iter++) {
// 方法1:使用GetIntArrayElements
{
jint* elements = env->GetIntArrayElements(array, nullptr);
for (int i = 0; i < length; i++) {
elements[i] = elements[i] * 2;
}
env->ReleaseIntArrayElements(array, elements, 0);
}
// 方法2:使用GetIntArrayRegion/SetIntArrayRegion
{
std::vector<jint> buffer(length);
env->GetIntArrayRegion(array, 0, length, buffer.data());
for (auto& val : buffer) {
val /= 2;
}
env->SetIntArrayRegion(array, 0, length, buffer.data());
}
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
__android_log_print(ANDROID_LOG_INFO, "PERF_TEST",
"数组访问 %d次耗时: %lld微秒",
iterations, duration.count());
}
};
九、常见问题与解决方案
9.1 JNI常见错误
cpp
// 错误1:JNI引用表溢出
void referenceTableOverflow(JNIEnv* env) {
// ❌ 错误:创建太多局部引用不释放
for (int i = 0; i < 1000; i++) {
jstring str = env->NewStringUTF("test");
// 忘记调用 env->DeleteLocalRef(str);
}
// ✅ 正确:及时释放局部引用
for (int i = 0; i < 1000; i++) {
jstring str = env->NewStringUTF("test");
// 处理str...
env->DeleteLocalRef(str);
}
}
// 错误2:跨线程使用JNIEnv
void crossThreadJNIError(JavaVM* vm) {
std::thread nativeThread([vm]() {
// ❌ 错误:直接使用其他线程的JNIEnv
// JNIEnv* env = ...;
// env->NewStringUTF("error");
// ✅ 正确:先附加线程
JNIEnv* env = nullptr;
vm->AttachCurrentThread(&env, nullptr);
if (env != nullptr) {
jstring str = env->NewStringUTF("correct");
// 处理...
env->DeleteLocalRef(str);
vm->DetachCurrentThread();
}
});
nativeThread.detach();
}
// 错误3:内存泄漏
void memoryLeakExample(JNIEnv* env) {
// ❌ 错误:创建全局引用不释放
jstring globalStr = static_cast<jstring>(env->NewGlobalRef(
env->NewStringUTF("leak")));
// 忘记调用 env->DeleteGlobalRef(globalStr);
// ✅ 正确:使用RAII管理
JNIGlobalRef<jstring> safeRef(env, env->NewStringUTF("safe"));
// 自动管理生命周期
}
9.2 最佳实践总结
- 优先使用动态注册:代码更清晰,维护更方便
- 缓存常用ID:在JNI_OnLoad中缓存类引用和方法ID
- 及时释放引用:局部引用在不再使用时立即释放
- 使用RAII包装:自动管理资源生命周期
- 线程安全:每个线程使用自己的JNIEnv
- 异常检查:每次JNI调用后检查异常
- 性能优化:减少JNI调用次数,批量处理数据
- 启用CheckJNI:开发阶段启用CheckJNI检测错误
十、学习路径与资源
10.1 推荐学习路径
markdown
第一阶段:基础(1-2周)
├── JNI数据类型映射
├── 静态/动态注册
├── 基本类型操作
└── 字符串处理
第二阶段:进阶(2-3周)
├── 数组操作
├── 对象操作与反射
├── 引用管理
└── 异常处理
第三阶段:高级(3-4周)
├── 多线程JNI
├── 性能优化
├── Android特定集成
└── 调试与测试
第四阶段:实战(持续)
├── 阅读Framework JNI代码
├── 实现复杂Native模块
└── 性能调优与问题排查
10.2 学习资源
-
官方文档:
-
开源项目:
- Android Framework源码(尤其看JNI部分)
- android-ndk
-
调试工具:
- addr2line:解析Native崩溃堆栈
- ndk-stack:自动符号化堆栈
- AddressSanitizer:内存错误检测
10.3 实践项目建议
- 基础项目:实现Java与C++的字符串转换工具
- 中级项目:实现图像处理的JNI接口
- 高级项目:实现一个完整的Native计算模块,包含多线程和异常处理
- Framework项目:阅读并理解Android Framework中的关键JNI模块
通过系统学习和实践,你将能够熟练掌握JNI开发,为深入Android Framework开发打下坚实基础。