Android源码学习——linker(1)

本文详述了Android中动态库的加载过程,从System.loadLibrary开始,探讨了如何通过Runtime和PathClassLoader找到SO文件路径,以及如何通过nativeLoad方法更新LD_LIBRARY_PATH并调用dlopen进行加载。在dlopen中,讲解了RTLD_LAZY、RTLD_NOW和RTLD_GLOBAL三个标志位的含义,并介绍了加载成功后的初始化操作。
摘要由CSDN通过智能技术生成

本文学习的源码参考AndroidXRef,版本为Lollipop 5.1.0_r1。


在Android开发过程中,我们要想使用JNI机制调用一个native方法时,首先会需要调用一个system的方法,把定义了native方法的库加载进来。今天,就从System.loadLibrary(“XXX”)来详细看一下一个动态库的加载过程。

    public static void loadLibrary(String libName) {
        Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
    }

这个loadLibrary是System的一个方法,同时也说明了JNI标准本身就是Java语言的一部分。
可以看到这个方法里实际调用了Runtime的loadLibrary方法。

void loadLibrary(String libraryName, ClassLoader loader) {
        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;
        }

        String filename = System.mapLibraryName(libraryName);
        List<String> candidates = new ArrayList<String>();
        String lastError = null;
        for (String directory : mLibPaths) {
            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);
    }

loadLibrary首先根据传入的loader,调用它的findLibrary(libraryName)方法去获取要加载的so的文件路径,然后判断一下获得的路径是否为空,不为空,则调用doLoad(filename, loader)去执行so的加载链接等等过程。
若传入的loader为空,则首先去通过System的mapLibraryName(libraryName)方法获取so的文件名,然后在一个mLibPaths的变量里面去找这个so文件,并最终拼接形成最终的so文件路径,最后还是调用doLoad(candidate, loader)去执行so的加载链接过程。

看下doLoad的实现:

private String doLoad(String name, ClassLoader loader) {
        String ldLibraryPath = null;
        if (loader != null && loader instanceof BaseDexClassLoader) {
            ldLibraryPath = ((BaseDexClassLoader) loader).getLdLibraryPath();
        }

        synchronized (this) {
            return nativeLoad(name, loader, ldLibraryPath);
        }
    }

注释中提到,Android应用从Zygote进程中创建出来,因此他们没有自定义的LD_LIBRARY_PATH,so文件的默认路径肯定也不在这个变量中;而PathClassLoader知道正确的路径,因此我们可以暂时先不加载依赖库,但是后面需要按依赖顺序加载。我们需要将API添加到Android的动态链接器中,这样我们就可以为当前运行的进程更新所使用的库路径,此处就是将classloader中的路径传递给nativeload。

可以看到doLoad先提取了ldLibraryPath的值,然后和so文件的名字以及loader一起传递给了nativeLoad函数。很显然,

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值