Android源码分析实战之JNI so库加载System.loadLibrary流程分析

本文深入探讨Android系统中JNI so库的加载过程,通过分析System.loadLibrary的源码,揭示从调用System.loadLibrary到触发JNI_OnLoad方法的详细步骤。以Android 7.1.1_r6源码为基础,逐步解析关键函数如Runtime的loadLibrary0方法、JVM_NativeLoad以及JavaVMExt的LoadNativeLibrary方法,旨在澄清动态注册过程中JNI库的加载原理。
摘要由CSDN通过智能技术生成

JNI so库加载流程之System.loadLibrary流程分析


最近在学习jni相关的知识,很多博客都说,jni***动态注册*时调用System.loadLibrary或者System.load方法加载so库,System.loadLibrary或System.load会调用到so库中的JNI_OnLoad方法进行方法注册,但是这个说是这样说,对于读者依然很模糊,到底System.loadLibrary或System.load到底是怎样的一种流程进行加载的并且调用JNI_OnLoad方法进行注册的呢,为了解开自己的疑惑,在这里看了一Android源码,做一下记录,也希望对别人有所帮助!

下面的代码都来自于android7.1.1_r6源码,除了第一个代码片作为影子其他代码片的第一行都标注了代码片的来源。

System.loadLibrary(libName);

loadLibrary是System.java中的一个静态方法

//libcore/ojluni/src/main/java/java/lang/System.java
    public static void loadLibrary(String libname) {
        Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
    }

getRuntime是Runtime.java中的方法,调用该方法返回Runtime对象,loadLibrary0是Runtime.java中的方法,见下面代码:

//libcore/ojluni/src/main/java/java/lang/Runtime.java
private static Runtime currentRuntime = new Runtime();//单例模式
public static Runtime getRuntime() {
        return currentRuntime;
    }

synchronized void loadLibrary0(ClassLoader loader, String libname) {
    if (libname.indexOf((int)File.separatorChar) != -1) {
        throw new UnsatisfiedLinkError(
"Directory separator should not appear in library name: " + libname);
    }//判断传入的库名称是否合法,比如我们的库是libxxx.so,我们只需要传入xxx就可以了
    String libraryName = libname;
    if (loader != null) {
  //如果类加载器不为空
        String filename = loader.findLibrary(libraryName);//查找是否存在我们需要的库文件
        if (filename == null) {
            // It's not necessarily true that the ClassLoader used
            // System.mapLibraryName, but the default setup does, and it's
            // misleading to say we didn't find "libMyLibrary.so" when we
            // actually searched for "liblibMyLibrary.so.so".
            throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
                                           System.mapLibraryName(libraryName) + "\"");
        }//不存在库文件则抛出异常
        String error = doLoad(filename, loader);//如果库文件存在,就加载
        if (error != null) {
            throw new UnsatisfiedLinkError(error);
        }//加载库文件失败,抛出异常
        return;
    }
//下面这些代码是类加载器为空的情况下才执行,正常情况下开发者app中开发者自己写的库文件加载时不会执行到这里,因为传入的类加载器不会为空,系统应用才有可能走这里,这时下面获取系统默认的库存放路径才是有用的
    String filename = System.mapLibraryName(libraryName);
    List<String> candidates = new ArrayList<String>();
    String lastError = null;
    for (String directory : getLibPaths()) {
  //getLibPaths()用来获取系统中存放so库的文件路径,下面有相关的实现代码和解释
            String candidate = directory + filename;//拼我们要加载库的绝对路径
            candidates.add(candidate);

            if (IoUtils.canOpenReadOnly(candidate)) {
  //判断绝对路径上的文件是否存在
                String error = doLoad(candidate, loader);//如果存在,并且是只可读,则加载该库
                if (error == null) {
                    return; // We successfully loaded the library. Job done.
                }//出现错误直接返回
                lastError = error;
            }
        }

        if (lastError != null) {
            throw new UnsatisfiedLinkError(lastError);
        }
        throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
    }
private String[] getLibPaths() {
        if (mLibPaths == null) {
            synchronized(this) {
                if (mLibPaths == null) {
                    mLibPaths = initLibPaths();//调用initLibPaths方法
                }
            }
        }
        return mLibPaths;
    }

private static String[] initLibPaths() {
        String javaLibraryPath = System.getProperty("java.library.path");//可以看出系统默认的库文件存放路径是在java.library.path属性中存储的
        if (javaLibraryPath == null) {
            return EmptyArray.STRING;
        }
        String[] paths = javaLibraryPath.split(":");
        // Add a '/' to the end of each directory so we don't have to do it every time.
        for (int i = 0; i < paths.length; ++i) {
            if (!paths[i].endsWith("/")) {
                paths[i] += "/";
            }
        }
        return paths;
    }


    private String doLoad(String name, ClassLoader loader) {
        // Android apps are forked from the zygote, so they can't have a custom LD_LIBRARY_PATH,
        // which means that by default an app's shared library directory isn't on LD_LIBRARY_PATH.

        // The PathClassLoader set up by frameworks/base knows the appropriate path, so we can load
        // libraries with no dependencies just fine, but an app that has multiple libraries that
        // depend on each other needed to load them in most-dependent-first order.

        // We added API to Android's dynamic linker so we can update the library path used for
        // the currently-running process. We pull the desired path out of the ClassLoader here
        // and pass it to nativeLoad so that it can call the private dynamic linker API.

        // We didn't just change frameworks/base to update the LD_LIBRARY_PATH once at the
        // beginning because multiple apks can run in the same process and third party code can
        // use its own BaseDexClassLoader.

        // We didn't just add a dlopen_with_custom_LD_LIBRARY_PATH call because we wanted any
        // dlopen(3) calls made from a .so's JNI_OnLoad to work too.

        // So, find out what the native library search path is for the ClassLoader in question...
        String librarySearchPath = null;
        if (loader != null && loader instanceof BaseDexClassLoader) {
            BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
            librarySearchPath = dexClassLoader.getLdLibraryPath();
        }
        // nativeLoad should be synchronized so there's only one LD_LIBRARY_PATH in use regardless
        // of how many ClassLoaders are in the system, but dalvik doesn't support synchronized
        // internal natives.
        synchronized (this) {
            return nativeLoad(name, loader, librarySearchPath);//调用本地方法nativeLoad,nativeLoad是一个本地方法
        }
    }


    // TODO: should be synchronized, but dalvik doesn't support synchronized internal natives.
    private 
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值