android系统动态库加载分析



     最近开发的应用apk出现了运行时加载so库失败问题,所以顺带研究下android查找和加载so库的过程.

     android加载动态库使用java.lang.System类(在libcore/luni/src/main/java/java/lang/System.java这个文件中),它提供了两套方法:

1.直接指定so库详细路径

public static void load(String pathName) {
        Runtime.getRuntime().load(pathName, VMStack.getCallingClassLoader());
    }

由上面的那段code,可以看到,它的实现非常简单,就只是先调用VMStack.getCallingClassLoader()获取到ClassLoader,然后再把实际要做的事情委托给了Runtime来做而已。
接下来我们再看一下Runtime.load()的实现(在libcore/luni/src/main/java/java/lang/Runtime.java这个文件中):

void load(String filename, ClassLoader loader) {
        if (filename == null) {
            throw new NullPointerException("library path was null.");
        }
        String error = nativeLoad(filename, loader);
        if (error != null) {
            throw new UnsatisfiedLinkError(error);
        }
    }
直接调用nativeLoad()这个native方法来load library.System类提供的第一种加载so库方法load()仅仅调用native code直接加载指定路径的so库 .nativeLoad()方法的具体实现后面介绍.
2.指定so库名称,系统自动查找并加载

/**
     * Loads and links the library with the specified name. The mapping of the
     * specified library name to the full path for loading the library is
     * implementation-dependent.
     *
     * @param libName
     *            the name of the library to load.
     * @throws UnsatisfiedLinkError
     *             if the library could not be loaded.
     */
    public static void loadLibrary(String libName) {
        Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
    }

同样委托Runtime来真正执行操作.

/*
     * Loads and links a library without security checks.
     */
    void loadLibrary(String libraryName, ClassLoader loader) {
        if (loader != null) {
            String filename = loader.findLibrary(libraryName);
            if (filename == null) {
                throw new UnsatisfiedLinkError("Couldn't load " + libraryName + ": " +
                        "findLibrary returned null");
            }
            String error = nativeLoad(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 (new File(candidate).exists()) {
                String error = nativeLoad(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);
    }

正常情况下,VMStack.getCallingClassLoader()获取到ClassLoader不会为空.故这里先分析loader != null分支.此时,首先使用调用类的ClassLoader查找该so库并获取其全路径名;然后同上调用native code直接加载指定路径的so库.


         而我们代码加载so库失败的原因就是ClassLoader中没搜索到对应的so库文件.从而运行时异常.先看下java.lang.ClassLoader的默认实现.

protected String findLibrary(String libName) {
        return null;
    }

基类ClassLoader为空实现.android中它的实际实现类是谁呢?android中直接打印实际运行的ClassLoader的String

05-11 08:18:57.857: V/MainActivity(11556): classLoader = dalvik.system.PathClassLoader[dexPath=/data/app/com.saroll.test.test-1.apk,libraryPath=/data/app-lib/com.test.test-1]

也就是说android应用内部类默认的ClassLoader为dalvik.system.PathClassLoader(位于libcore\dalvik\src\main\java\dalvik\system.PathClassLoader),它其实只是简单的继承BaseDexClassLoader而已,没有任何实际的内容 .直接进入BaseDexClassLoader中看看 findLibrary() 真正的实现( 在libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java这个文件中 ):

public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;

    /**
     * Constructs an instance.
     *
     * @param dexPath the list of jar/apk files containing classes and
     * resources, delimited by {@code File.pathSeparator}, which
     * defaults to {@code ":"} on Android
     * @param optimizedDirectory directory where optimized dex files
     * should be written; may be {@code null}
     * @param libraryPath the list of directories containing native
     * libraries, delimited by {@code File.pathSeparator}; may be
     * {@code null}
     * @param parent the parent class loader
     */
    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    }

    @Override
    public String findLibrary(String name) {
        return pathList.findLibrary(name);
    }

    @Override public String toString() {
        return getClass().getName() + "[" + pathList + "]";
    }
}

  

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值