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

attachBaseContext中调用loadLibrary时如何在JNI_OnLoad获取Context

问题描述
  • 需求:需要在JNI_OnLoad执行阶段获取Context对象,受场景限制,必须在attachBaseContext()方法内而非onCreate()方法中调用System.loadLibrary()加载native库
  • 异常表现:参考公开实现编写代码后,在attachBaseContext()中加载库时,JNI层获取到的context返回值为NULL;将System.loadLibrary()调用逻辑移动到onCreate()中时,同一段代码可正常执行

相关实现代码如下:

// CustomApplication.java
public class CustomApplication extends Application {
    @Override
    protected void attachBaseContext(Context context) {
        super.attachBaseContext(context);
        System.loadLibrary("hook_main");
    }

    // @Override
    // public void onCreate() {
    //     super.onCreate();
    //     System.loadLibrary("hook_main");
    // }
}
// hook_main.c
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    // ...
    jclass activityThread = (*env)->FindClass(env, "android/app/ActivityThread");
    jmethodID currentActivityThread = (*env)->GetStaticMethodID(env, activityThread, "currentActivityThread", "()Landroid/app/ActivityThread;");
    jobject at = (*env)->CallStaticObjectMethod(env, activityThread, currentActivityThread);
    jmethodID getApplication = (*env)->GetMethodID(env, activityThread, "getApplication", "()Landroid/app/Application;");
    // at和getApplication均不为NULL
    jobject context = (*env)->CallObjectMethod(env, at, getApplication); 
    // 获取到的context为NULL
    return JNI_VERSION;
}
根因分析

出现该问题的核心原因是Android Application生命周期执行顺序:

  • 调用super.attachBaseContext(context)时,仅完成了当前Application实例的base Context挂载,此时ActivityThread类中mApplication字段还未完成赋值,因此调用getApplication()方法必然返回NULL
  • 当生命周期走到onCreate()时,ActivityThread已经完成了Application实例的绑定赋值,此时再加载库调用getApplication()就能拿到正常的实例对象
可行解决方案

方案1:主动传参(推荐,无兼容问题)

这是稳定性最高的实现方式,完全不依赖系统隐藏API,不受Android版本升级影响:

  1. 加载完native库后,主动定义一个native初始化方法,将attachBaseContext拿到的Context参数直接传入JNI层
  2. JNI层收到Context后,创建全局引用保存即可使用

代码示例:
Java层修改:

public class CustomApplication extends Application {
    @Override
    protected void attachBaseContext(Context context) {
        super.attachBaseContext(context);
        System.loadLibrary("hook_main");
        // 主动传入Context到native层
        nativeHookInit(context);
    }

    /**
     * native初始化方法,传入Context
     */
    private native void nativeHookInit(Context context);
}

Native层对应实现:

// 全局保存Context引用
jobject g_global_context = NULL;

JNIEXPORT void JNICALL
Java_对应包名_CustomApplication_nativeHookInit(JNIEnv *env, jobject thiz, jobject context) {
    // 创建全局引用,避免局部引用随方法结束被回收
    g_global_context = (*env)->NewGlobalRef(env, context);
    // 后续初始化逻辑直接使用g_global_context即可
}

方案2:反射获取已初始化的Context(不推荐,有兼容风险)

如果必须在JNI_OnLoad中直接获取、不修改Java层代码,可以反射ActivityThread内部的LoadedApk对象,从LoadedApk中拿到已经创建的ContextImpl实例。但该方式依赖系统隐藏API,Android 9及以上版本受隐藏API访问限制,不同厂商系统可能修改内部字段名,稳定性差,非必要不使用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 20:30:45