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

Android 11下如何在新创建进程中获取JVM指针?

核心结论

首先明确一个底层规则:通过Runtime.getRuntime().exec()拉起的是完全独立的原生进程,进程内不存在任何已初始化的Android Runtime(ART)实例,没有现成的JavaVM指针可以获取。
JVM/ART实例是进程私有的资源,主进程内缓存的JavaVM*属于主进程独立虚拟地址空间,跨进程传递后在新进程中是完全无效的野地址,不存在直接复用主进程JVM的可能。你dlopen JNI库时不会触发JNI_OnLoad也是必然结果——纯原生进程中没有运行ART,自然不会执行JNI库的加载回调。


可行实现方案

方案1:使用Android官方多进程机制(兼容性最好,无权限要求,推荐)

这是Android平台唯一全版本稳定支持、可以正常使用JNI能力的实现方式:

  • 放弃Runtime.exec()拉起独立命令行进程的逻辑,在应用Manifest中声明运行在独立进程的Service/ContentProvider组件,通过Binder触发该进程内的业务逻辑
  • 该类组件由Zygote进程fork孵化,启动时已经完成ART环境初始化,你通过System.loadLibrary()加载JNI库时,系统会自动调用JNI_OnLoad并传入有效的JavaVM*指针,直接缓存即可使用
  • 如果原有逻辑是命令行形式的原生代码,可以将代码编译为静态/动态库,在多进程组件的JNI方法中dlopen调用,无需自行处理JVM初始化逻辑

方案2:在原生进程内手动启动ART实例(仅适用于系统应用/root场景)

如果你必须在exec拉起的独立原生进程中运行逻辑,且持有系统签名或root权限,可以手动加载ART运行时创建JVM实例,Android 11上的实现步骤如下:

  1. 绕过系统库加载限制,dlopen系统的libart.so,解析出JNI_CreateJavaVM导出函数

    注意Android 11启用了严格的系统库命名空间隔离,普通第三方应用直接dlopen libart.so会触发权限拒绝,该步骤仅系统应用、root权限进程可完成

  2. 按照ART启动规范填充JavaVMInitArgs参数,必须在启动参数中配置正确的dex classpath,否则后续无法找到你定义show_image方法的类
  3. 调用JNI_CreateJavaVM拿到有效的JavaVM*和当前线程的JNIEnv*,后续即可正常执行JNI调用、缓存jclass和jmethodID
    核心逻辑参考代码:
    // 定义函数指针类型
    typedef int (*JNI_CreateJavaVM_Impl)(JavaVM** pvm, JNIEnv** penv, JavaVMInitArgs* args);
    JavaVM* jvm = nullptr;
    JNIEnv* env = nullptr;
    
    // 加载ART库,普通应用无权限执行该步骤
    void* art_handle = dlopen("libart.so", RTLD_NOW);
    auto create_vm = (JNI_CreateJavaVM_Impl)dlsym(art_handle, "JNI_CreateJavaVM");
    
    // 填充VM启动参数,需配置dex路径、JNI版本等信息
    JavaVMInitArgs vm_args;
    // ... 按实际需求填充vm_args内容
    int ret = create_vm(&jvm, &env, &vm_args);
    if (ret == JNI_OK) {
        // 初始化成功,可正常执行JNI逻辑
        jclass glojc = env->FindClass("对应类的全限定名");
        jmethodID showimage = env->GetStaticMethodID(glojc, "show_image", "([F[II)V");
    }
    

    该方案在Android 12及以上版本限制进一步收紧,非系统进程几乎无法成功,不建议普通第三方应用依赖。

方案3:IPC回调主进程执行Java逻辑(无权限要求,适配所有Android版本)

如果你拉起新进程只是为了执行计算、IO等原生逻辑,不需要在新进程内直接操作Java层资源,可以把需要调用Java方法的逻辑跨进程打回主进程执行:

  • 新进程和主进程提前建立Unix Domain Socket、匿名管道等本地IPC通道
  • 新进程中需要触发show_image时,将待传递的浮点数组、整型参数序列化后通过IPC通道发送给主进程
  • 主进程收到数据后,在自身的ART环境中调用对应的Java方法完成图片展示,新进程完全不需要初始化JVM环境
  • 该方案是普通第三方应用在Android 11上实现该需求最稳妥的方案,不触碰系统隐藏API,也不需要特殊权限。

不可行方案避坑
  • 尝试读取主进程内存获取JVM指针:Android从5.0开始默认开启进程地址空间隔离,非调试/root进程无权读取其他进程内存,就算拿到指针值在新进程中也完全无效
  • 尝试从系统全局符号、属性中获取通用JVM指针:不存在全局共享的JVM实例,每个进程的ART环境独立,未启动ART的进程中不存在相关实例
  • 指望手动dlopen JNI库时自动拿到JNIEnv:JNI_OnLoad的回调逻辑是ART加载so时主动触发的,纯原生进程中没有ART运行时,dlopen操作只会执行so的构造函数,不会传入任何JNI相关指针。

内容的提问来源于stack exchange,提问作者ccsssau

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 02:45:47