android基础之ClassLoader类加载器

0.前言:

大家都知道安卓系统运行APP流程

  1. Java类会被编译成一个或者多个字节码文件(.class)然后打包到JAR文件
  2. Java虚拟机会从相应的CLASS文件和JAR文 件中获取相应的字节码
  3. 通过一个工具(dx)将应用所有的 CLASS文件转换成一个DEX文件
  4. Dalvik虚拟机会从其中读取指令和数据

如果想知道jvm和Dalvik可以看看我之前写的Java基础之Dalvik和JVM的认识

而加载dex,jar的就是类加载器

关于dex的数量问题,如果你不分dex基本上就一个.

1.认识android中的ClassLoader

  1. 可以看得出来父类就是ClassLoader,当然android系统一般用的是PathClassLoader,而我们使用热修复,插件化用的基本就是DexClassLoader
  2. 同时类加载机制是双亲委派,父类先加载没如果加载不了,子类再去加载

基础上面两点基本上面试上没问题了

so,我们继续了解

1.1 ClassLoader(基于Api25) 源码了解

1.1.1 构造

    protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }

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

可以看出ClassLoader主要就是传入一个父构造器,如果你不传呢,就会去创建一个

默认父构造器为一个PathClassLoader且此PathClassLoader父构造器为BootClassLoader

还记得我说过的系统基本用的就是PathClassLoader

1.1.2 loadClass 加载Class

    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);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    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) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                }
            }
            return c;
    }
    //寻找class
    protected final Class findLoadedClass(String name) {
        ClassLoader loader;
        if (this == BootClassLoader.getInstance())
            loader = null;
        else
            loader = this;
        return VMClassLoader.findLoadedClass(loader, name);
    }

    protected Class findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

这段代码就是告诉你双亲委托是啥意思,父类加载不了了,子类去加载,其中findClass需要子类去实现

1.2 了解一下ClassLoader家族成员

1.2.1 BootClassLoader(Class最终父类)

和java虚拟机中不同的是BootClassLoader是ClassLoader内部类,由java代码实现而不是c++实现,是Android平台上所有ClassLoader的最终parent,这个内部类是包内可见,所以我们没法使用

1.2.2 URLClassLoader(只能加载jar)

只能用于加载jar文件,但是由于 dalvik 不能直接识别jar,所以在 Android 中无法使用这个加载器。

1.2.3 BaseDexClassLoader

PathClassLoader和DexClassLoader都继承自BaseDexClassLoader,其中的主要逻辑都是在BaseDexClassLoader完成的

他的构造方法还比较重要

  public BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent) {
    throw new RuntimeException("Stub!");
  }

BaseDexClassLoader的构造函数包含四个参数,分别为:

dexPath:

指目标类所在的APK或jar文件的路径,类装载器将从该路径中寻找指定的目标类,该类必须是APK或jar的全路径.如果要包含多个路径,路径之间必须使用特定的分割符分隔,特定的分割符可以使用System.getProperty(“path.separtor”)获得。上面”支持加载APK、DEX和JAR,也可以从SD卡进行加载”指的就是这个路径,最终做的是将dexPath路径上的文件ODEX优化到内部位置optimizedDirectory,然后,再进行加载的。

File optimizedDirectory:

由于dex文件被包含在APK或者Jar文件中,因此在装载目标类之前需要先从APK或Jar文件中解压出dex文件,该参数就是制定解压出的dex 文件存放的路径。这也是对apk中dex根据平台进行ODEX优化的过程。其实APK是一个程序压缩包,里面包含dex文件,ODEX优化就是把包里面的执行程序提取出来,就变成ODEX文件,因为你提取出来了,系统第一次启动的时候就不用去解压程序压缩包的程序,少了一个解压的过程。这样的话系统启动就加快了。为什么说是第一次呢?是因为DEX版本的也只有第一次会解压执行程序到 /data/dalvik-cache(针对PathClassLoader)或者optimizedDirectory(针对DexClassLoader)目录,之后也是直接读取目录下的的dex文件,所以第二次启动就和正常的差不多了。当然这只是简单的理解,实际生成的ODEX还有一定的优化作用。ClassLoader只能加载内部存储路径中的dex文件,所以这个路径必须为内部路径。

libPath:

指目标类中所使用的C/C++库存放的路径

classload:

是指该装载器的父装载器,一般为当前执行类的装载器,例如在Android中以context.getClassLoader()作为父装载器。

1.2.4 DexClassLoader

  public DexClassLoader(String dexPath, ClassLoader parent) {
    super((String)null, (File)null, (String)null, (ClassLoader)null);
    throw new RuntimeException("Stub!");
  }

DexClassLoader支持加载APK、DEX和JAR,也可以从SD卡进行加载。

上面说dalvik不能直接识别jar,DexClassLoader却可以加载jar文件,这难道不矛盾吗?

其实在BaseDexClassLoader里对”.jar”,”.zip”,”.apk”,”.dex”后缀的文件最后都会生成一个对应的dex文件,所以最终处理的还是dex文件,而URLClassLoader并没有做类似的处理。

一般我们都是用这个DexClassLoader来作为动态加载的加载器。

1.2.5 PathClassLoader

  public PathClassLoader(String dexPath, ClassLoader parent) {
    super((String)null, (File)null, (String)null, (ClassLoader)null);
    throw new RuntimeException("Stub!");
  }

PathClassLoader是用来加载Android系统类和应用的类,并且不建议开发者使用。

而在art虚拟机上PathClassLoader可以加载未安装的apk的dex!

2. ClassLoader加载class的过程

以下代码不是API25的

#BaseDexClassLoader
@Override
protected Class findClass(String name) throws ClassNotFoundException { 
    Class clazz = pathList.findClass(name);
    if (clazz == null) { 
        throw new ClassNotFoundException(name); 
    } 
    return clazz;
}
#DexPathList
public Class findClass(String name) { 
    for (Element element : dexElements) { 
        DexFile dex = element.dexFile;
        if (dex != null) { 
            Class clazz = dex.loadClassBinaryName(name, definingContext); 
          if (clazz != null) { 
              return clazz; 
          } 
        } 
    } 
    return null;
}
#DexFile
public Class loadClassBinaryName(String name, ClassLoader loader) { 
    return defineClass(name, loader, mCookie);
}
private native static Class defineClass(String name, ClassLoader loader, int cookie);

可以看出,BaseDexClassLoader中有个pathList对象,pathList中包含一个DexFile的数组dexElements,

由上面分析知道,dexPath传入的原始dex(.apk,.zip,.jar等)文件在optimizedDirectory文件夹中生成相应的优化后的odex文件

dexElements数组就是这些odex文件的集合,如果不分包一般这个数组只有一个Element元素,也就只有一个DexFile文件

而对于类加载呢,就是遍历这个集合,通过DexFile去寻找。最终调用native方法的defineClass。

3.ART虚拟机和Dalvik区别

Dalvik是通过JIT(即时编译器)优化字节码转换成机器码,这会拖慢应用的运行效率

而ART通过AOT(预编译)在应用第一安装时使用dex2oat工具,把.dex文件转化成OAT文件

一个即时,一个预编译,可以看出那个好点

你可能感兴趣的:(安卓基础的回顾)