美文网首页
Android中ClassLoader源码解析(一)类关系

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

作者: Codetend | 来源:发表于2018-07-25 00:35 被阅读0次

    背景

    在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<IOException> suppressedExceptions = new ArrayList<IOException>();
            // 根据传入的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<File> 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<File> files, File optimizedDirectory,
              List<IOException> 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源码解析(一)类关系

          本文链接:https://www.haomeiwen.com/subject/uhsumftx.html