1-2-3 Kotlin与C++基础-JNI原理与使用

🏗️ JNI原理与使用深度解析

一、JNI基础概念与架构

1.1 JNI是什么?

JNI(Java Native Interface) 是Java平台提供的一个编程框架,允许:

  1. Java代码调用Native代码(C/C++)
  2. Native代码调用Java代码
  3. 在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 最佳实践总结

  1. 优先使用动态注册:代码更清晰,维护更方便
  2. 缓存常用ID:在JNI_OnLoad中缓存类引用和方法ID
  3. 及时释放引用:局部引用在不再使用时立即释放
  4. 使用RAII包装:自动管理资源生命周期
  5. 线程安全:每个线程使用自己的JNIEnv
  6. 异常检查:每次JNI调用后检查异常
  7. 性能优化:减少JNI调用次数,批量处理数据
  8. 启用CheckJNI:开发阶段启用CheckJNI检测错误

十、学习路径与资源

10.1 推荐学习路径

markdown 复制代码
第一阶段:基础(1-2周)
    ├── JNI数据类型映射
    ├── 静态/动态注册
    ├── 基本类型操作
    └── 字符串处理

第二阶段:进阶(2-3周)
    ├── 数组操作
    ├── 对象操作与反射
    ├── 引用管理
    └── 异常处理

第三阶段:高级(3-4周)
    ├── 多线程JNI
    ├── 性能优化
    ├── Android特定集成
    └── 调试与测试

第四阶段:实战(持续)
    ├── 阅读Framework JNI代码
    ├── 实现复杂Native模块
    └── 性能调优与问题排查

10.2 学习资源

  1. 官方文档

  2. 开源项目

    • Android Framework源码(尤其看JNI部分)
    • android-ndk
  3. 调试工具

    • addr2line:解析Native崩溃堆栈
    • ndk-stack:自动符号化堆栈
    • AddressSanitizer:内存错误检测

10.3 实践项目建议

  1. 基础项目:实现Java与C++的字符串转换工具
  2. 中级项目:实现图像处理的JNI接口
  3. 高级项目:实现一个完整的Native计算模块,包含多线程和异常处理
  4. Framework项目:阅读并理解Android Framework中的关键JNI模块

通过系统学习和实践,你将能够熟练掌握JNI开发,为深入Android Framework开发打下坚实基础。

相关推荐
TimeFine2 小时前
Android AI解放生产力(六)实战:解放页面开发前的繁琐工作
android·架构
心静财富之门2 小时前
a.py打包加密
android
成都大菠萝3 小时前
1-2-2 Kotlin与C++基础-C++基础语法与内存管理
android
陈希瑞3 小时前
【保姆级教程】安卓手机免Root一键部署AutoGLM:支持语音控制与自动化操作
android·智能手机·自动化
TheNextByte13 小时前
如何将联系人从Android传输到计算机的 6 种方法
android
喂_balabala3 小时前
excludeFromRecents
android
TimeFine3 小时前
Android AI解放生产力(五)实战:解放写API接口的繁琐工作
android
csj505 小时前
安卓基础之《(6)—Activity组件(3)》
android
怀旧,6 小时前
【Linux系统编程】13. Ext系列⽂件系统
android·linux·缓存