美文网首页安卓工具相关
从JNI_OnLoad看so的加载

从JNI_OnLoad看so的加载

作者: 静默加载 | 来源:发表于2019-07-26 20:07 被阅读38次

    前言

    最近在看 FlutterDartJava 使用 MethodChannel 进行通信相关的代码,有上层一直跟到了底层。最后看到了 MethodChannel 的注册是在 JNI_OnLoad 的方法中。这个方法是在 so 被加载的时候调用的。今天主要从so 的加载看一下 JNI_OnLoad 的调用。

    Flutter的so加载

    我们先从 Application 的代码看起:

    FlutterApplication.onCreate;
    |--FlutterMain.startInitialization(Context applicationContext);
    |--|--FlutterMain.startInitialization(Context applicationContext, FlutterMain.Settings settings);
    
    public static void startInitialization(Context applicationContext, FlutterMain.Settings settings) {
        if (Looper.myLooper() != Looper.getMainLooper()) {
            throw new IllegalStateException("startInitialization must be called on the main thread");
        } else if (sSettings == null) {
            sSettings = settings;
            long initStartTimestampMillis = SystemClock.uptimeMillis();
            initConfig(applicationContext);
            initAot(applicationContext);
            initResources(applicationContext);
            System.loadLibrary("flutter");
            long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;
            nativeRecordStartTimestamp(initTimeMillis);
        }
    }
    

    startInitialization 我们调用了 System.loadLibrary("flutter") 进行 flutterso 加载。

    so的加载

    AndroidP源码:

    System.loadLibrary(libName);
    |--Runtime.loadLibrary0(libName,classLoader);
    |--|--|--Runtime.nativeLoad(name,loader,ldLibraryPath);
    |--|--|--Runtime_nativeLoad(JNIEnv* env, jclass, jstring javaFilename, jobject javaLoader, jstring javaLdLibraryPath);
    |--|--|--|--JVM_NativeLoad(env, javaFilename, javaLoader);
    |--|--|--|--JavaVMExt::LoadNativeLibrary(const std::string& path,Handle<mirror::ClassLoader> class_loader,std::string* detail)
    |--|--|--|--|--android::OpenNativeLibrary(dlopen);
    |--|--|--|--|--|--SharedLibrary.FindSymbol;
    |--|--|--|--|--|--SharedLibrary.FindSymbolWithoutNativeBridge(dlsym);
    
    bool JavaVMExt::IsBadJniVersion(int version) {
      // We don't support JNI_VERSION_1_1. These are the only other valid versions.
      return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;
    }
    bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
                                      const std::string& path,
                                      jobject class_loader,
                                      std::string* error_msg) {
      /******部分代码省略******/
      bool was_successful = false;
      void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
      if (sym == nullptr) {
        VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
        was_successful = true;
      } else {
        ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
        self->SetClassLoaderOverride(class_loader);
        VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
        //定义一个名为JNI_OnLoadFn的函数指针,参数为(JavaVM*, void*),返回值类型为int
        typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
        //将JNI_OnLoadFn指向sym也就是JNI_OnLoad函数
        JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
        //调用JNI_OnLoad方法,返回jni版本号
        int version = (*jni_on_load)(this, nullptr);
        if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) {
          EnsureFrontOfChain(SIGSEGV);
        }
        self->SetClassLoaderOverride(old_class_loader.get());
        if (version == JNI_ERR) {//JNI_ERR=-1
          StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
        } else if (JavaVMExt::IsBadJniVersion(version)) {//错误的jni版本
          StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
                        path.c_str(), version);
        } else {
          was_successful = true;
        }
        VLOG(jni) << "[Returned " << (was_successful ? "successfully" : "failure")
                  << " from JNI_OnLoad in \"" << path << "\"]";
      }
      library->SetResult(was_successful);
      return was_successful;
    }
    

    上面这个过程我们证明了 so 的加载会调用 JNI_OnLoad 。 其中 Runtime_nativeLoad 方法是通过动态注册实现的。

    相同的我们也可以看 JNI_OnUnload 方法,当虚拟机释放该C库时,则会调用JNI_OnUnload()函数来进行善后清除动作。

    dlopen、dlsym

    使用dlopen,dlsym调用JNI_OnLoad方法;

    • dlopen以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程;
    • dlerror返回出现的错误;
    • dlsym通-过句柄和连接符名称获取函数名或者变量名;
    • dlclose来卸载打开的库;

    dlfcn.c 文件:

    #ifndef DLFCN_H
    #define DLFCN_H
    #ifdef __cplusplus
    extern "C" {
    #endif
    #if defined(DLFCN_WIN32_EXPORTS)
    #   define DLFCN_EXPORT __declspec(dllexport)
    #else
    #   define DLFCN_EXPORT
    #endif
    #define RTLD_LAZY   0
    #define RTLD_NOW    0
    #define RTLD_GLOBAL (1 << 1)
    #define RTLD_LOCAL  (1 << 2)
    #define RTLD_DEFAULT    ((void *)0)
    #define RTLD_NEXT       ((void *)-1)
    DLFCN_EXPORT void *dlopen ( const char *file, int mode );
    DLFCN_EXPORT int   dlclose(void *handle);
    DLFCN_EXPORT void *dlsym(void *handle, const char *name);
    DLFCN_EXPORT char *dlerror(void);
    #ifdef __cplusplus
    }
    #endif
    #endif /* DLFCN_H */
    

    native方法的动态注册

    前面我们就有讲过在 so 被加载之后会调用 JNI_OnLoad 方法,我们这次反过来看一下 JNI_OnLoad 加载 native 方法。

    在 /libcore/ojluni/src/main/native/Register.cpp 文件中有一个 JNI_OnLoad 方法,它在内部进行了 register_java_lang_Runtime(env); 的注册。register_java_lang_Runtime 的实现在 /libcore/ojluni/src/main/native/Runtime.c 文件中:

    JNIEXPORT jstring JNICALL
    Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
                       jobject javaLoader){
        return JVM_NativeLoad(env, javaFilename, javaLoader);
    }
    static JNINativeMethod gMethods[] = {
      FAST_NATIVE_METHOD(Runtime, freeMemory, "()J"),
      FAST_NATIVE_METHOD(Runtime, totalMemory, "()J"),
      FAST_NATIVE_METHOD(Runtime, maxMemory, "()J"),
      NATIVE_METHOD(Runtime, gc, "()V"),
      NATIVE_METHOD(Runtime, nativeExit, "(I)V"),
      //java.lang.Runtime.nativeLoad方法的native注册
      NATIVE_METHOD(Runtime, nativeLoad,
                    "(Ljava/lang/String;Ljava/lang/ClassLoader;)"
                        "Ljava/lang/String;"),
    };
    
    void register_java_lang_Runtime(JNIEnv* env) {
      jniRegisterNativeMethods(env, "java/lang/Runtime", gMethods, NELEM(gMethods));
    }
    

    总结

    从上面的过程分析我们看到了 so 的加载以及它的注册 JNI_OnLoad 和反注册 JNI_OnUnload 方法的调用,以及 native 方法的注册。

    文章到这里就全部讲述完啦,若有其他需要交流的可以留言哦

    想阅读作者的更多文章,可以查看我 个人博客 和公共号:

    振兴书城

    相关文章

      网友评论

        本文标题:从JNI_OnLoad看so的加载

        本文链接:https://www.haomeiwen.com/subject/dldrrctx.html