关于ClassLoader

2020-03-29  本文已影响0人  瑜小贤

一. Java中的ClassLoader

1. 继承关系

二. Android中的ClassLoader

1. 继承关系

2. 作用和分类

(1)作用:

Android虚拟机运行的是Dex字节码(将Class文件合并优化生成的产物),ClassLoader用来加载dex文件。

(2)分类

分为系统类加载器和自定义类加载器。主要的系统类加载器:

三. Android中ClassLoader源码分析

相关源码:
/dalvik/system

1. ClassLoader

(1)构造函数:
protected ClassLoader() {
    this(checkCreateClassLoader(), getSystemClassLoader());
}
protected ClassLoader(ClassLoader parent) {
    this(checkCreateClassLoader(), parent);
}

ClassLoader需要传入一个父ClassLoader,如果父ClassLoader不传的时候,通过
getSystemClassLoader方法,创建了一个PathClassLoader

public static ClassLoader getSystemClassLoader() {
    return SystemClassLoader.loader;
}
  static private class SystemClassLoader {
    public static ClassLoader loader = ClassLoader.createSystemClassLoader();
}
private static ClassLoader createSystemClassLoader() {
    String classPath = System.getProperty("java.class.path", ".");
    String librarySearchPath = System.getProperty("java.library.path", "");
    return new PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance());
}
(2)loadClass方法:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
    // First, check if the class has already been loaded
    //(1)先判断这个类是否被加载过
    Class<?> c = findLoadedClass(name);
    if (c == null) {
        try {
            //(2)如果没被加载,先让父加载器进行加载
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {
            // ClassNotFoundException thrown if class not found
            // from the non-null parent class loader
        }
        //(3)如果父加载器加载失败,则自身进行加载
        if (c == null) {
            // If still not found, then invoke findClass in order
            // to find the class.
            c = findClass(name);
        }
    }
    return c;
}

双亲委托机制:

  1. 除了顶层的类加载器外,其他的类加载器都有自己的父类加载器,在加载类时首先判断这个类是否被加载过,如果已经加载则直接返回。
  2. 如果未被加载过,则先尝试让父加载器进行加载,最终所有加载请求都会传递给顶层的加载器中。
  3. 当父加载器发现未找到所需的类而无法完成加载请求时,子加载器的findClass方法中进行加载。

2. BaseDexClassLoader

(1)构造:
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
        String librarySearchPath, ClassLoader parent) {
    this(dexPath, librarySearchPath, parent, null, false);
}

public BaseDexClassLoader(String dexPath,
        String librarySearchPath, ClassLoader parent, ClassLoader[] sharedLibraryLoaders,
        boolean isTrusted) {
    super(parent);
    this.sharedLibraryLoaders = sharedLibraryLoaders == null
            ? null
            : Arrays.copyOf(sharedLibraryLoaders, sharedLibraryLoaders.length);
    this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
    if (reporter != null) {
        reportClassLoaderChain();
    }
}
(2)DexPathList

DexPathList中维护着一个Element数组,这个数组中Element元素就是Dex文件(做热修复就要在这里做文章了)

#DexPathList
 /*package*/ static class Element {
 
    @UnsupportedAppUsage
    private final File path;
    /** Whether {@code path.isDirectory()}, or {@code null} if {@code path == null}. */
    private final Boolean pathIsDirectory;
    @UnsupportedAppUsage
    private final DexFile dexFile;
    private ClassPathURLStreamHandler urlHandler;
    private boolean initialized;
    
    @UnsupportedAppUsage
    public Element(DexFile dexFile, File dexZipPath) {
        if (dexFile == null && dexZipPath == null) {
            throw new NullPointerException("Either dexFile or path must be non-null");
        }
        this.dexFile = dexFile;
        this.path = dexZipPath;
        // Do any I/O in the constructor so we don't have to do it elsewhere, eg. toString().
        this.pathIsDirectory = (path == null) ? null : path.isDirectory();
    }
    public Element(DexFile dexFile) {
        this(dexFile, null);
    }
    public Element(File path) {
        this(null, path);
    }
    ......
}

3. BootClassLoader

Android平台上所有ClassLoader的最终parent。Android系统启动时会使用BootClassLoader来预加载常用类。

class BootClassLoader extends ClassLoader {
    private static BootClassLoader instance;
    @FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
    public static synchronized BootClassLoader getInstance() {
        if (instance == null) {
            instance = new BootClassLoader();
        }
        return instance;
    }
...
}

BootClassLoader的访问修饰符是默认的,只有在同一个包中才可以访问

4. PathClassLoader

public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
    super(dexPath, null, librarySearchPath, parent);
}

@libcore.api.CorePlatformApi
public PathClassLoader(
        String dexPath, String librarySearchPath, ClassLoader parent,
        ClassLoader[] sharedLibraryLoaders) {
    super(dexPath, librarySearchPath, parent, sharedLibraryLoaders);
}

使用PathClassLoader来加载系统类和应用程序的类,通常用来加载已安装的apk的dex文件
构造函数没有optimizedDirectory,无法定义dex文件路径,该参数默认值为/data/dalvik-cache

5. DexClassLoader

public DexClassLoader(String dexPath, String optimizedDirectory,
        String librarySearchPath, ClassLoader parent) {
    super(dexPath, null, librarySearchPath, parent);
}

DexClassLoader可以加载dex文件以及包含dex的压缩文件(apk和jar文件),在其父类BaseDexClassLoader里对".jar",".zip",".apk",".dex"后缀的文件最后都会生成一个对应的dex文件。

四. Android加载Class的过程

1. BaseDexClassLoader的findClass

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
    // First, check whether the class is present in our shared libraries.
    if (sharedLibraryLoaders != null) {
        for (ClassLoader loader : sharedLibraryLoaders) {
            try {
                return loader.loadClass(name);
            } catch (ClassNotFoundException ignored) {
            }
        }
    }
    // Check whether the class in question is present in the dexPath that
    // this classloader operates on.
    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;
}

内部调用了DexPathList的findClass方法

#DexPathList
private Element[] dexElements;
......
public Class<?> findClass(String name, List<Throwable> suppressed) {
    for (Element element : dexElements) {
        Class<?> clazz = element.findClass(name, definingContext, suppressed);
        if (clazz != null) {
            return clazz;
        }
    }
    if (dexElementsSuppressedExceptions != null) {
        suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
    }
    return null;
}

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

DexPathList内部包含了一个DexFile的数组dexElements。类加载的过程,就是遍历这个数组调用了DexFileloadClassBinaryName方法,最终调用native方法defineClassNative

感谢https://blog.csdn.net/www851903307/article/details/87017662

上一篇下一篇

猜你喜欢

热点阅读