类加载

类加载

image.png
public abstract class ClassLoader {

ClassLoader位于/jre/librt.jar/java/lang/ClassLoader.java中,是一个抽象类。

public abstract class ClassLoader {
    ......
    class BootClassLoader extends ClassLoader {
        ......
    }
}

BootClassLoader是ClassLoader的内部类,用于加载系统的类,如Activity。

public class BaseDexClassLoader extends ClassLoader {

    private static volatile Reporter reporter = null;

    private final DexPathList pathList;//实际的findclass类

BaseDexClassLoader继承ClassLoader

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);
    }
}
public class DexClassLoader extends BaseDexClassLoader {
   
    public DexClassLoader(String dexPath, String optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
    }
}

PathClassLoader和DexClassLoader 都继承了 BaseDexClassLoader。有种说法是PathClassLoader只能加载已经安装过的apk中的class文件,而DexClassLoader可以用来加载未安装的apk的class文件。

这种说法实际是不正确的。在8.0(API26)之前,两者的区别在于DexClassLoader 的第二个参数optimizedDirectory,可以传入优化的dex文件,即odex的路径。而如源码中所示,8.0以后,两者都传入null,就没有什么区别了。

DexClassLoader classLoader = new DexClassLoader( apkPath, mOptFile.getAbsolutePath(), null, mContext.getClassLoader());
classLoader .loadClass( "com.cwh.plugin.Test" );

我们通过创建一个DexClassLoader对象,调用loadClass方法来加载一个类,来了解类的加载流程。

双亲委托

loadClass方法定义在ClassLoader中

public abstract class ClassLoader { 
  ......

 public Class loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, false);
    }

//类加载方法
 protected Class loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
            //查找这个类是否已经加载了,加载则直接返回
            Class c = findLoadedClass(name);
            //还没有加载
            if (c == null) {
                try {
                    //父类如果不为null,委托给父类
                    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
                }

              //如果父类没有找到,则子类自己查找
                if (c == null) {
                   c = findClass(name);
                }
            }
            return c;
    }

    //通过虚拟机进行查找
    protected final Class findLoadedClass(String name) {
        ClassLoader loader;
        if (this == BootClassLoader.getInstance())
            loader = null;
        else
            loader = this;
        //通过本地方法findLoadedClass查找
        return VMClassLoader.findLoadedClass(loader, name);
      }

   private Class findBootstrapClassOrNull(String name){
        return null;
    }

  //空方法,由子类实现
   protected Class findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

如源码所示,加载过程有3层判断:

  • 先通过本地方法findLoadedClass查找是否已加载过此类,如果找到,则直接将类返回。
  • 如果还未加载,会通过parent的loadClass来加载。
  • 如果parent没有加载到,则自己再进行加载。

第一层判断是,通过本地方法调用虚拟机来查找类是否已加载,不做分析。
第二层判断就是双亲委托,即先交给parent来加载。这个parent是DexClassLoader的最后一个参数,mContext.getClassLoader()。

   Log.i( TAG, "加载器:" + mContext.getClassLoader());
   加载器: dalvik.system.PathClassLoader

mContext的PathClassLoader由系统生成,方法为createSystemClassLoader()。

public abstract class ClassLoader {
   private static ClassLoader createSystemClassLoader() {
        String classPath = System.getProperty("java.class.path", ".");
        String librarySearchPath = System.getProperty("java.library.path", "");
        //parent为BootClassLoader
        return new PathClassLoader(classPath, librarySearchPath, BootClassLoader.getInstance());
    }

由打印日志和PathClassLoader的创建知道,DexClassLoader 的parent是PathClassLoader,而PathClassLoader的parent是BootClassLoader ,因此,双亲委托,最终委托到了BootClassLoader 类。下面看BootClassLoader 的loadClass方法

public abstract class ClassLoader {

    ......

    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;
      }

      public BootClassLoader() {
        super(null);
      }     
          
      //parent类加载方法
      @Override
      protected Class loadClass(String className, boolean resolve)
           throws ClassNotFoundException {
       //又查了一遍是否已加载
        Class clazz = findLoadedClass(className);
        //如果没有再通过findClass加载
        if (clazz == null) {
            clazz = findClass(className);
        }
        return clazz;
      }

      //调用Class的本地静态方法classForName加载
      @Override
      protected Class findClass(String name) throws ClassNotFoundException {
          return Class.classForName(name, false, null);
      }
    
}
public final class Class implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {

        //类加载方法
      @FastNative
      static native Class classForName(String className, boolean shouldInitialize,
            ClassLoader classLoader) throws ClassNotFoundException;
      }

我们看到,parent又进行了一次查询类是否已加载,如果没有,则调用Class的本地方法classForName,来加载我们传入的类。

如果parent也没没有成功加载到我们的类,将进入第三层判断,调用ClassLoader 的findClass来加载。而ClassLoader 的findClass是一个空实现,由上面分析知道,BaseDexClassLoader 继承了ClassLoader,因此,我们来看BaseDexClassLoader 的findClass方法。

public class BaseDexClassLoader extends ClassLoader {
    //实际的findclass类
    private final DexPathList pathList;

    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        this(dexPath, optimizedDirectory, librarySearchPath, parent, false);
    }

    
    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String librarySearchPath, ClassLoader parent, boolean isTrusted) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);

        if (reporter != null) {
            reportClassLoaderChain();
        }
    }

    //子类自己的类加载方法
    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        List suppressedExceptions = new ArrayList();
        //实际由DexPathList 来加载
        Class c = pathList.findClass(name, suppressedExceptions);//实际调用DexPathList的findClass
        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 来进行加载的。我们来分析DexPathList 类。

final class DexPathList {
    private static final String DEX_SUFFIX = ".dex";//文件后缀为.dex
    private static final String zipSeparator = "!/";
    //类加载器
    private final ClassLoader definingContext;

   //Element数组
    private Element[] dexElements;

    //类加载
    public Class findClass(String name, List suppressed) {
        //遍历Element数组
        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;
    }

  static class Element {
      private final File path;//dex 的路径

      private final DexFile dexFile;//dex文件对象
      
      public Class findClass(String name, ClassLoader definingContext,
                List suppressed) {
            //由DexFile的loadClassBinaryName进行查找
            return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                    : null;
        }
  }

DexPathList 遍历成员变量Element 数组,Element 是DexPathList 的静态内部类,一个.dex文件封装成一个DexFile对象,一个DexFile对象封装成一个Element 对象。

二进制文件的读取最终通过DexFile的loadClassBinaryName来读取。

public final class DexFile {

    //通过defineClass加载.dex二进制文件
     public Class loadClassBinaryName(String name, ClassLoader loader, List suppressed) {
        return defineClass(name, loader, mCookie, this, suppressed);
    }

    //加载.dex二进制文件
    private static Class defineClass(String name, ClassLoader loader, Object cookie,
                                     DexFile dexFile, List suppressed) {
        Class result = null;
        try {
            //由本地方法defineClassNative进行加载
            result = defineClassNative(name, loader, cookie, dexFile);
        } catch (NoClassDefFoundError e) {
            if (suppressed != null) {
                suppressed.add(e);
            }
        } catch (ClassNotFoundException e) {
            if (suppressed != null) {
                suppressed.add(e);
            }
        }
        return result;
    }

  //本地方法defineClassNative加载.dex二进制文件
  private static native Class defineClassNative(String name, ClassLoader loader, Object cookie,
                                                  DexFile dexFile)

一个apk可能会有多个.dex文件,以防止65535问题,因此,DexPathList 定义了一个Element数组来保存。

你可能感兴趣的:(类加载)