Android JNI方法的注册

前言

前面讲ART虚拟机启动的时候说到,在init创建第一个横跨java和native的进程zygote的时候,会通过startVm创建虚拟机,VM创建完成后,会通过startReg完成虚拟机的JNI方法注册

startReg

// /frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{

    // 此处ART虚拟机已经分析
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);
    // 创建jni
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

startVM就是创建虚拟机,紧接着在虚拟机创建完成后,调用了startReg

/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        return -1;
    }
}

调用register_jni_procs来注册jni方法,而这些方法就是存放在gRegJNI中

static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
    for (size_t i = 0; i < count; i++) {
        if (array[i].mProc(env) < 0) {
            return -1;
        }
    }
    return 0;
}
static const RegJNIRec gRegJNI[] = {
    REG_JNI(register_android_util_SeempLog),
    REG_JNI(register_com_android_internal_os_RuntimeInit),
    REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
    ...
}

REG_JNI是一个宏方法的定义

    #define REG_JNI(name)      { name, #name }
    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
        const char* mName;
    };

可见,调用mProc就是调用其参数名指向的函数。比如第一个

REG_JNI(register_android_util_SeempLog)
// android_util_SeempLog.cpp
static JNINativeMethod gMethods[] = {
    /* name, signature, funcPtr */
    { "seemp_println_native",  "(ILjava/lang/String;)I",
            (void*) android_util_SeempLog_println_native },
};
int register_android_util_SeempLog(JNIEnv* env)
{
    jclass clazz = env->FindClass("android/util/SeempLog");
    if (clazz == NULL) {
        return -1;
    }

    return AndroidRuntime::registerNativeMethods(env, "android/util/SeempLog", gMethods,
            NELEM(gMethods));
}

调用了registerNativeMethods,而传入的参数是gMethods,也就是java层函数名和jni层函数的映射关系

/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
    const char* className, const JNINativeMethod* gMethods, int numMethods)
{
    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
// JNIHelp.cpp
MODULE_API int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
    const JNINativeMethod* gMethods, int numMethods)
{
    int result = e->RegisterNatives(c.get(), gMethods, numMethods);
}
// jni.h
    jint RegisterNatives(jclass clazz, const JNINativeMethod *methods,
                         jint nMethods) {
        return functions->RegisterNatives(this,clazz,methods,nMethods);
    }

unctions是指向JNINativeInterface结构体指针,该过程完成了java层方法到native层方法的映射。

loadlibrary

而我们平时正常使用jni的时候,都是需要调用loadLibrary来加载so库,那就从这里开始jni的注册流程

// System.java
    public static void loadLibrary(String libname) {
        Runtime.getRuntime().loadLibrary0(Reflection.getCallerClass(), libname);
    }
// Runtime.java
    void loadLibrary0(Class<?> fromClass, String libname) {
        ClassLoader classLoader = ClassLoader.getClassLoader(fromClass);
        loadLibrary0(classLoader, fromClass, libname);
    }
// This overload exists for @UnsupportedAppUsage
    void loadLibrary0(ClassLoader loader, String libname) {
        // Pass null for callerClass, we don't know it at this point. Passing null preserved
        // the behavior when we used to not pass the class.
        loadLibrary0(loader, null, libname);
    }
    private synchronized void loadLibrary0(ClassLoader loader, Class<?> callerClass, String libname) {
        String filename = System.mapLibraryName(libraryName);
        String error = nativeLoad(filename, loader, callerClass);
    }
    private static String nativeLoad(String filename, ClassLoader loader) {
        return nativeLoad(filename, loader, null);
    }

    private static native String nativeLoad(String filename, ClassLoader loader, Class<?> caller);
// Runtime.c
JNIEXPORT jstring JNICALL
Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
                   jobject javaLoader, jclass caller)
{
    return JVM_NativeLoad(env, javaFilename, javaLoader, caller);
}
// openjdkjvm.cc
JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
                                 jstring javaFilename,
                                 jobject javaLoader,
                                 jclass caller) {

    art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
    bool success = vm->LoadNativeLibrary(env,
                                         filename.c_str(),
                                         javaLoader,
                                         caller,
                                         &error_msg);
}
// java_vm_ext.cc
bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
                                  const std::string& path,
                                  jobject class_loader,
                                  jclass caller_class,
                                  std::string* error_msg) {

    // 通过dlopen的方式打开so库
      void* handle = android::OpenNativeLibrary(
            env,
            runtime_->GetTargetSdkVersion(),
            path_str,
            class_loader,
            (caller_location.empty() ? nullptr : caller_location.c_str()),
            library_path.get(),
            &needs_native_bridge,
            &nativeloader_error_msg);

    // 通过dlsystem的方式找到JNI_OnLoad方法
      void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
          using JNI_OnLoadFn = int(*)(JavaVM*, void*);
    JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
                                  }

// /system/core/libnativeloader/native_loader.cpp

void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
                        jobject class_loader, const char* caller_location, jstring library_path,
                        bool* needs_native_bridge, char** error_msg) {
        void* handle = dlopen(path, RTLD_NOW);
                        }

以上其实就是一个so库加载的过程了,通过JNI_OnLoad方法,而jnionload方法中,多是会调用registerNativeMethods,也就是上面说到了,所以殊途同归,

JNI的动态注册和静态注册

动态注册

动态注册也就是我们上面说到的,通过实现JNI_OnLoad方法,来主动建立java层函数和native函数的映射关系,这样逻辑清晰,并且不会有效率的损失。

静态注册

静态注册也就是我们平常使用的通过javah来生成的文件,这种方式程序运行效率低,因为初次调用natvie函数是需要根据函数名在jni层是搜索对应的本地函数(通过JNIEXPORT和JNICALL宏实现),然后监理对应的关系,这个过程会比较耗时。