You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android NDK下如何用JNI_GetCreatedJavaVMs按需获取JNIEnv调用Java代码?

编译错误修复与跨平台JNIEnv按需获取方案

一、解决JNI_GetCreatedJavaVMs未定义的编译错误

方案1:仅支持Android API Level 31+

  • 升级NDK到r23或更高版本(你的r18b版本过旧,不支持该函数的符号导出,r23开始完整适配API31的系统库导出规则)。
  • 配置项目最低SDK版本为31:
    • 若用Android.mk:添加LOCAL_MIN_SDK_VERSION := 31
    • 若用CMake:在模块的build.gradle中设置android.defaultConfig.ndk.minSdkVersion=31,或在CMakeLists.txt中定义CMAKE_ANDROID_MIN_SDK_VERSION=31
  • 配置完成后,链接器会自动从系统库中找到JNI_GetCreatedJavaVMs的符号。

方案2:兼容API Level 31以下版本

低版本Android系统未导出JNI_GetCreatedJavaVMs,需通过全局保存JavaVM实例的方式绕过:

  1. 在核心.so中定义全局JavaVM指针:
    static JavaVM* g_global_jvm = nullptr;
    
  2. 重写JNI_OnLoad方法(每个.so加载时都会触发),保存JVM实例:
    extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) {
        g_global_jvm = vm;
        return JNI_VERSION_1_6; // 根据业务需求返回对应JNI版本
    }
    
  3. 多.so共享JVM实例:
    • 若有多个.so需要调用Java,将保存JVM的逻辑放在基础依赖.so中,其他.so依赖该库;
    • 或导出一个C接口供其他.so获取JVM:
      extern "C" JNIEXPORT JavaVM* JNICALL GetGlobalJavaVM() {
          return g_global_jvm;
      }
      
  4. 此方案无需依赖JNI_GetCreatedJavaVMs,彻底解决编译错误。

二、跨平台.so按需获取JNIEnv的实现

无论是用保存的JVM还是API31+的JNI_GetCreatedJavaVMs,通用实现逻辑如下:

核心获取函数

#include <jni.h>

// 全局JVM指针(兼容低版本Android)
static JavaVM* g_global_jvm = nullptr;

JNIEnv* acquire_jni_env() {
    JNIEnv* env = nullptr;
    JavaVM* vm = nullptr;

#if defined(ANDROID) && __ANDROID_API__ >= 31
    // Android API31+:通过JNI_GetCreatedJavaVMs获取JVM
    jint ret = JNI_GetCreatedJavaVMs(&vm, 1, nullptr);
    if (ret != JNI_OK || vm == nullptr) {
        return nullptr;
    }
#else
    // 低版本Android或其他平台:使用全局保存的JVM实例
    vm = g_global_jvm;
    if (vm == nullptr) {
        return nullptr;
    }
#endif

    // 获取当前线程的JNIEnv
    jint env_ret = vm->GetEnv((void**)&env, JNI_VERSION_1_6);
    if (env_ret == JNI_EDETACHED) {
        // 线程未附加到JVM,尝试附加
        if (vm->AttachCurrentThread(&env, nullptr) != JNI_OK) {
            return nullptr;
        }
        // 注意:后台原生线程附加后,必须在退出前调用DetachCurrentThread()
    } else if (env_ret != JNI_OK) {
        return nullptr;
    }

    return env;
}

// 可选:释放JNIEnv(针对附加的后台线程)
void release_jni_env() {
    if (g_global_jvm == nullptr) return;
    g_global_jvm->DetachCurrentThread();
}

跨平台关键注意事项

  • 线程安全:JNIEnv是线程局部资源,每个线程需独立获取;后台原生线程附加JVM后,必须在退出前调用DetachCurrentThread(),避免内存泄漏。
  • 平台适配:非Android平台(如Windows、Linux)的标准JNI环境中,JNI_GetCreatedJavaVMs是原生支持的,直接使用即可;Android低版本依赖JNI_OnLoad保存的JVM实例。
  • 无平台硬依赖:用宏(如ANDROID、__ANDROID_API__)区分平台逻辑,确保.so代码可在不同环境编译运行。

三、不推荐的方案

若强行在低版本Android中使用JNI_GetCreatedJavaVMs,需动态加载libandroid_runtime.so并通过dlsym获取函数地址,但该方法不稳定——不同Android版本的库路径、符号名可能存在差异,极易导致运行时崩溃,不建议采用。

内容的提问来源于stack exchange,提问作者Surya Prakash Reddy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 12:15:17