Android中ClassLoader源码解析(一)类关系

背景

在Android开发过程中,大家对于动态加载代码一定不陌生。在应用各个开源框架的过程中多多少少有接触,其主要原理离不开ClassLoader相关的类。这里,我们会从Android中ClassLoader相关类的源码入手,更好的理解和学习动态加载类的原理。

类源码解析

在平时我们做类动态加载的时候,会使用到DexClassLoader这个类,直接从zip包或者apk包或者直接加载dex文件,然后调用loadClass方法来获得外部加载的类,使用方法大致如下:

File apkFile = new File(getFilesDir(), "loader.apk");
DexClassLoader dexClassLoader = new DexClassLoader(
                  apkFile.getAbsolutePath(), getCodeCacheDir().getAbsolutePath(), null, getClassLoader());
Class clazz = dexClassLoader.loadClass("com.codetend.plugin.MainActivity");

那么我们从这个类入手,查看源码:

public class DexClassLoader extends BaseDexClassLoader {
      public DexClassLoader(String dexPath, String optimizedDirectory,String librarySearchPath, ClassLoader parent) {
          super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
      }
}

我们可以看到这个类十分简单,其功能实现基本都交给了父类BaseDexClassLoader。这个类相对内容比较多,我们从构造方法开始看起:

public BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent) {
    super(parent);
    //这里源码是Android8.0的,最后一个传参为null。意味着DexClassLoader与PathClassLoader已经没有区别了
    this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);
    if (reporter != null) {
        reporter.report(this.pathList.getDexPaths());
    }
}

BaseDexClassLoader构造的时候,新建了一个DexPathList的对象,把我们给传入的参数全部透传进去了。这个DexPathList是十分重要的一个类。同样,从构造方法开始查看源码:

public DexPathList(ClassLoader definingContext, String dexPath, String librarySearchPath, File optimizedDirectory) {
         //检查传参判空
        if (definingContext == null) {
            throw new NullPointerException("definingContext == null");
        }
        if (dexPath == null) {
            throw new NullPointerException("dexPath == null");
        }
        if (optimizedDirectory != null) {
            if (!optimizedDirectory.exists())  {
                throw new IllegalArgumentException(
                        "optimizedDirectory doesn't exist: "
                        + optimizedDirectory);
            }
            if (!(optimizedDirectory.canRead()
                            && optimizedDirectory.canWrite())) {
                throw new IllegalArgumentException(
                        "optimizedDirectory not readable/writable: "
                        + optimizedDirectory);
            }
        }
        this.definingContext = definingContext;
        // 崩溃栈保存
        ArrayList suppressedExceptions = new ArrayList();
        // 根据传入的dex路径,保存成Element数组
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                           suppressedExceptions, definingContext);
        // 传入的library路径,加上系统的library路径,构建File list
        this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);
        this.systemNativeLibraryDirectories =
                splitPaths(System.getProperty("java.library.path"), true);
        List allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
        allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
        // 根据File list,保存成NativeLibraryElement数组
        this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);
     
        if (suppressedExceptions.size() > 0) {
            this.dexElementsSuppressedExceptions =
                suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
        } else {
            dexElementsSuppressedExceptions = null;
        }
   } 

构建该类的基础,就是生成两种数组:ELementNativeLibraryElement。从字面上的意思,很容易能猜到这两个数组分别代表了dex元素和so库元素。事实也正如我们所猜想。这里我们先只查看dex元素的构建。从方法makeDexElements查看:

private static Element[] makeDexElements(List files, File optimizedDirectory,
          List suppressedExceptions, ClassLoader loader) {
    Element[] elements = new Element[files.size()];
    int elementsPos = 0;
    for (File file : files) {
        if (file.isDirectory()) {
            //如果file是文件夹,那么可以用于寻找资源(可暂时忽略,和类加载无关)
            elements[elementsPos++] = new Element(file);
        } else if (file.isFile()) {
            String name = file.getName();
            if (name.endsWith(DEX_SUFFIX)) {
                //如果是纯粹的dex文件,则生成无dexZipPath的Element
                try {
                    DexFile dex = loadDexFile(file, optimizedDirectory, loader, elements);
                    if (dex != null) {
                        elements[elementsPos++] = new Element(dex, null);
                    }
                } catch (IOException suppressed) {
                    System.logE("Unable to load dex file: " + file, suppressed);
                    suppressedExceptions.add(suppressed);
                }
            } else {
                //如果是apk或者zip包(内含dex文件),则生成有dexZipPath(保存zip包路径)的Element
                DexFile dex = null;
                try {
                    dex = loadDexFile(file, optimizedDirectory, loader, elements);
                } catch (IOException suppressed) {
                    suppressedExceptions.add(suppressed);
                }

                if (dex == null) {
                    elements[elementsPos++] = new Element(file);
                } else {
                    elements[elementsPos++] = new Element(dex, file);
                }
            }
        } else {
            System.logW("ClassLoader referenced unknown path: " + file);
        }
    }
    //想不懂为什么数组会无法对齐?
    if (elementsPos != elements.length) {
        elements = Arrays.copyOf(elements, elementsPos);
    }
    return elements;
}

你可能感兴趣的:(Android中ClassLoader源码解析(一)类关系)