• JVM加载的是class文件,系统提供的类加载器(BootStrapClassLoader、ExtensionClassLoader、ApplicationClassLoader)
  • 安卓虚拟机加载的是dex文件,系统提供的dex加载器(DexClassLoader、PathClassLoader)

class文件、资源文件、AndroidManifest.xml 经过编译打包,成为apk安装包;apk后缀修改为.zip,解压后可以看到dex文件。

1、classLoader代码测试

在Activity的onCreate()中添加如下代码,得到当前classLoader,并递归父classLoader

ClassLoader classLoader = getClassLoader();
 if (classLoader != null){
      while (classLoader.getParent()!=null){
          classLoader = classLoader.getParent();
      }
   }

当前classLoader(PathClassLoader):dalvik.system.PathClassLoader[DexPathList[[zip file “/data/app/com.example.dexloader-rEcmqp5bzgybFOqTwgSCzg==/base.apk”],nativeLibraryDirectories=[/data/app/com.example.dexloader-rEcmqp5bzgybFOqTwgSCzg==/lib/x86, /system/lib, /system/vendor/lib]]]

父classLoader(BootClassLoader):java.lang.BootClassLoader@c25f209

2、Android Dex ClassLoader源码解析

源码地址:
https://www.androidos.net.cn/sourcecode

相关类:

  • ClassLoader抽象类,持有parent,提供loadClass()方法
  • BootClassLoader:Android系统启动时创建,用于加载一些系统Framework层级需要的类,APP启动的时候也会把这个BootClassLoader实例传进来。
  • BaseDexClassLoader :DexClassLoader的基类
  • PathClassLoader:
  • DexClassLoader:
  • DexPathList:持有dexElements,nativeLibraryElements,静态内部类Element、静态内部类NativeLibraryElement
  • DexFile:
2.1、ClassLoader抽象类加载器

抽象类

  • 判断当前类加载器是否已经加载过指定类,若已加载则直接返回,否则继续执行
  • 调用parent的类加载递归加载该类,检测是否加载,若已加载则直接返回,否则继续执行
  • 调用当前类加载器,通过findClass加载
public abstract class ClassLoader {

 private final ClassLoader parent;

 public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }
    
 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException  {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name); //步骤一,判断当前classLoader是否已经加载过该类
            if (c == null) {
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false); //步骤二,递归父classloader的loadClass()
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                }

                if (c == null) {
                    c = findClass(name); //步骤三,以上均没找到,使用当前classloader去加载
                }
            }
            return c;
    }

private Class<?> findBootstrapClassOrNull(String name) {  return null; }
    
 protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
2.2、BaseDexClassLoader
  • 成员变量pathList,是DexPathList对象
  • findClass、findResources、findLibrary都是通过pathList实现的
public class BaseDexClassLoader extends ClassLoader {

private final DexPathList pathList;
/**
 * dexPath 需要装载的APK或者Jar文件的路径。包含多个路径用File.pathSeparator间隔开,在Android上默认是 “:”
 * optimizedDirectory 优化后的dex文件存放目录,这里其实没用到,不知道是不是因为后来放弃了
 * libraryPath 目标类中使用的C/C++库so文件的路径,每个目录用File.pathSeparator间隔开; 可以为null
 * parent 该类装载器的父装载器,一般用当前执行类的装载器
 */
    public BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent) {
        super(parent);
        this.pathList = new dalvik.system.DexPathList(this, dexPath, librarySearchPath, null);

        if (reporter != null) {
            reporter.report(this.pathList.getDexPaths());
        }
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
        Class c = pathList.findClass(name, suppressedExceptions);
        if (c == null) {
            ClassNotFoundException cnfe = new ClassNotFoundException(
                    "Didn't find class \"" + name + "\" on path: " + pathList);
            for (Throwable t : suppressedExceptions) {
                cnfe.addSuppressed(t);
            }
            throw cnfe;
        }
        return c;
    }

    @Override
    protected URL findResource(String name) {
        return pathList.findResource(name);
    }

    @Override
    public String findLibrary(String name) {
        return pathList.findLibrary(name);
    }
}
2.3、DexClassLoader
  • 只是简单的继承BaseDexClassLoader
  • 提供一个构造函数 dexPath、librarySearchPath、parent
  • optimizedDirectory父类没有用到
public class DexClassLoader extends BaseDexClassLoader {

    public DexClassLoader(String dexPath, String optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), librarySearchPath, parent);
    }
}
2.3、PathClassLoader
  • 只是简单的继承BaseDexClassLoader
  • 提供两个构造函数,一个可以指定librarySearchPath,一个不指定librarySearchPath
public class PathClassLoader extends BaseDexClassLoader {
   
    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }

    public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
    }
}
2.4、DexPathList
  • findClass(String name, List suppressed),遍历dexElements,返回Class<?>对象
  • findResource(String name),遍历dexElements,返回URL对象
  • findLibrary(String libraryName),遍历nativeLibraryPathElements,返回library的全路径
final class DexPathList {
    private static final String DEX_SUFFIX = ".dex";
    private static final String zipSeparator = "!/";

    private final ClassLoader definingContext;
    private Element[] dexElements; 
    private final NativeLibraryElement[] nativeLibraryPathElements;

	/**
	 *  给dexElements、nativeLibraryPathElements赋值
	*/
    public DexPathList(ClassLoader definingContext, String dexPath,
            String librarySearchPath, File optimizedDirectory) {
        this.definingContext = definingContext;
        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                           suppressedExceptions, definingContext);

        this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);
        this.systemNativeLibraryDirectories = splitPaths(System.getProperty("java.library.path"), true);
        List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
        allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);
		 this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);
    }

    private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
            List<IOException> suppressedExceptions, ClassLoader loader) {
      return elements;
    }

    /**
     * 构建一个DexFile实例
     */
    private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader, Element[] elements)
            throws IOException {
            return new DexFile(file, loader, elements);
    }

 	 /**
     * 遍历dexElements中的每个Element,通过Element去查找class
     */
    public Class<?> findClass(String name, List<Throwable> suppressed) {
        for (Element element : dexElements) {
            Class<?> clazz = element.findClass(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }
        return null;
    }

 	 /**
     * 遍历dexElements中的每个Element,通过Element去查找Resource
     */
    public URL findResource(String name) {
        for (Element element : dexElements) {
            URL url = element.findResource(name);
            if (url != null) {
                return url;
            }
        }
        return null;
    }
    
	 /**
     * 遍历nativeLibraryPathElements中的每个NativeLibraryElement,通过NativeLibraryElement去查找library
     */
    public String findLibrary(String libraryName) {
        String fileName = System.mapLibraryName(libraryName);
        for (NativeLibraryElement element : nativeLibraryPathElements) {
            String path = element.findNativeLibrary(fileName);
            if (path != null) {
                return path;
            }
        }
        return null;
    }


   static class Element {
        private final File path;
        private final DexFile dexFile;

        public Element(DexFile dexFile, File dexZipPath) {
            this.dexFile = dexFile;
            this.path = dexZipPath;
        }

        public Class<?> findClass(String name, ClassLoader definingContext,
                List<Throwable> suppressed) {
            return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed) : null;
        }

        public URL findResource(String name) {
            if (path != null && path.isDirectory()) {
                File resourceFile = new File(path, name);
                if (resourceFile.exists()) {
                    try {
                        return resourceFile.toURI().toURL();
                    } catch (MalformedURLException ex) {
                        throw new RuntimeException(ex);
                    }
                }
            }
            return null;
        }
    }

  static class NativeLibraryElement {
        //全删了
    }
}
2.5 DexFile
public final class DexFile {
    private Object mCookie;
    private Object mInternalCookie;
    private final String mFileName;

    DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements) throws IOException {
        mCookie = openDexFile(fileName, null, 0, loader, elements);
        mInternalCookie = mCookie;
        mFileName = fileName;
        //System.out.println("DEX FILE cookie is " + mCookie + " fileName=" + fileName);
    }

 	public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
        return defineClass(name, loader, mCookie, this, suppressed);
    }

    private static Class defineClass(String name, ClassLoader loader, Object cookie,
                                     DexFile dexFile, List<Throwable> suppressed) {
        Class result = null;
        try {
            result = defineClassNative(name, loader, cookie, dexFile);
        } catch (NoClassDefFoundError e) {}
        return result;
    }
    
    //native方法去查找
    private static native Class defineClassNative(String name, ClassLoader loader, Object cookie, DexFile dexFile)
}

总结:

android glide加载铺满 android 加载中_加载


热修复核心逻辑: 在DexPathList.findClass()过程,一个Classloader可以包含多个dex文件,每个dex文件被封装到一个Element对象,这些Element对象排列成有序的数组dexElements。当查找某个类时,会遍历所有的dex文件,如果找到则直接返回,不再继续遍历dexElements。也就是说当两个类不同的dex中出现,会优先处理排在前面的dex文件,这便是热修复的核心精髓,将需要修复的类所打包的dex文件插入到dexElements前面。

参考:
Android插件化基础1-----加载SD上APKAndroid类加载器ClassLoader