final class DexPathList { ... // List of dex/resource (class path) elements.意思是class文件转化结果:dex文件集合 private Element[] dexElements;
// List of native library path elements. 意思是native层so文件集合 // 在热修复技术中so修复可以将新的so库路径追加到此数组之前达到热修效果,其实它是将例如xxx/xxx/abc.so分别以dir:xxx/xxx/、zip:abc.so这样区分存储而已,为了使用方便 private final Element[] nativeLibraryPathElements;
// List of application native library directories. 意思是native层so文件的路径集合,只表示路径。 private final List<File> nativeLibraryDirectories;
// List of system native library directories.意思是系统so文件路径集合 private final List<File> systemNativeLibraryDirectories;
... }
static class Element { private final File dir; // 路径 private final boolean isDirectory; // 该Element是否是文件夹 private final File zip; // 路径下对应的文件 private final DexFile dexFile; // 路径下对应的dex文件 }
类加载机制了解简单了解完开始分析System类。
System.load():
1 2 3
public static void load(String filename) { Runtime.getRuntime().load0(VMStack.getStackClass1(), filename); }
synchronized void load0(Class fromClass, String filename) { if (!(new File(filename).isAbsolute())) { throw new UnsatisfiedLinkError( "Expecting an absolute path of the library: " + filename); } if (filename == null) { throw new NullPointerException("filename == null"); } // 最终调用此方法去load so文件,其内部是去调用native方法 String error = doLoad(filename, fromClass.getClassLoader()); if (error != null) { throw new UnsatisfiedLinkError(error); } }
/** * 注释大概意思是我们日常应用进程是从Zygote进程fork出来的所以都是公用的同一个LD_LIBRARY_PATH路径。 * 开放这个方法是为了用户能动态的加载不同路径下的so库而不是只有启动的时候从单一的目录加载一次。 * 理由是因为一个进程可能会运行多个apk并且用户可能会手动实现自己的BaseDexClassLoader, * 所以有必要为用户提供一个差异化so加载的能力 */ 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; // 调用BaseDexClassLoader中的方法用于将所有native的lib路径以:拼接在一起然后给native层去load so文件 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) { // 最终调用native方法去load so库。Android所有的so库都是通过这个方法加载进内存的 return nativeLoad(name, loader, librarySearchPath); } }
// TODO: should be synchronized, but dalvik doesn't support synchronized internal natives. private static native String nativeLoad(String filename, ClassLoader loader, String librarySearchPath);
synchronized void loadLibrary0(ClassLoader loader, String libname) { if (libname.indexOf((int)File.separatorChar) != -1) { // 如果传入的是带/的文件路径则抛异常,这个类型的异常如果进行ndk相关开发肯定见过 throw new UnsatisfiedLinkError( "Directory separator should not appear in library name: " + libname); } String libraryName = libname; if (loader != null) {
// 从类加载器中查找so文件名并以全路径返回,类加载器中查找so时的路径是在其构造的时候指定的。 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; }