Android类加载(二)——双亲委托机制

Android类加载(一)——DVM、ART、Dexopt、DexAot名词解析
Android类加载(二)——双亲委托机制
Android类加载(三)——源码解读

Java类加载器

定义
  • BootClassLoader
    用于加载Android FrameWork层的class文件(系统的Activity)
  • PathClassLoader
    用于加载Android应用程序的class 文件(自己写的MainActivity)
    也可以加载指定的dex,以及jar、zip、apk中的classes.dex
  • DexClassLoader
    加载指定的dex,以及jar、zip、apk中的classes.dex

比较PathClassLoader和DexClassLoader

  • 在一个app安装的过程中,Android系统是没有用到DexClassLoader类的,而PathClassLoader会被用于加载Android应用程序的class 文件(例如自己写的MainActivity),DexClassLoader类就是提供给开发者使用的类,Android FrameWork层并没有使用到DexClassLoader。
  • 构造方法的区别
public class DexClassLoader extends BaseDexClassLoader {
    /**
     * Creates a {@code DexClassLoader} that finds interpreted and native
     * code.  Interpreted classes are found in a set of DEX files contained
     * in Jar or APK files.
     *
     * 

The path lists are separated using the character specified by the * {@code path.separator} system property, which defaults to {@code :}. * * @param dexPath the list of jar/apk files containing classes and * resources, delimited by {@code File.pathSeparator}, which * defaults to {@code ":"} on Android * @param optimizedDirectory directory where optimized dex files * should be written; must not be {@code null} * @param libraryPath the list of directories containing native * libraries, delimited by {@code File.pathSeparator}; may be * {@code null} * @param parent the parent class loader */ public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) { super(dexPath, new File(optimizedDirectory), libraryPath, parent); } }

public class PathClassLoader extends BaseDexClassLoader {
    /**
     * Creates a {@code PathClassLoader} that operates on a given list of files
     * and directories. This method is equivalent to calling
     * {@link #PathClassLoader(String, String, ClassLoader)} with a
     * {@code null} value for the second argument (see description there).
     *
     * @param dexPath the list of jar/apk files containing classes and
     * resources, delimited by {@code File.pathSeparator}, which
     * defaults to {@code ":"} on Android
     * @param parent the parent class loader
     */
    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }

    /**
     * Creates a {@code PathClassLoader} that operates on two given
     * lists of files and directories. The entries of the first list
     * should be one of the following:
     *
     * 
    *
  • JAR/ZIP/APK files, possibly containing a "classes.dex" file as * well as arbitrary resources. *
  • Raw ".dex" files (not inside a zip file). *
* * The entries of the second list should be directories containing * native library files. * * @param dexPath the list of jar/apk files containing classes and * resources, delimited by {@code File.pathSeparator}, which * defaults to {@code ":"} on Android * @param libraryPath the list of directories containing native * libraries, delimited by {@code File.pathSeparator}; may be * {@code null} * @param parent the parent class loader */ public PathClassLoader(String dexPath, String libraryPath, ClassLoader parent) { super(dexPath, null, libraryPath, parent); } }

从源码里可以看出它们都派生于BaseDexClassLoader类,但在它们的构造方法稍有不同

public DexClassLoader(String dexPath, String optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
public PathClassLoader(String dexPath, String libraryPath,
           ClassLoader parent) {
       super(dexPath, null, libraryPath, parent);
   }

有三个参数是一样的

  • dexPath:要加载的dex所在的目录
  • libraryPath:Native方法so文件所在的目录
  • parent:可以理解为双亲加载机制里的所谓的“父亲”(双亲委托机制下面再讲)
  • optimizedDirectory:opt优化后的dex文件所在的目录(而且从源码的注释里可以看到,这个目录必须为私有目录,不能为sd卡的目录)

而DexClassLoader就比PathClassLoader多了一个optimizedDirectory参数,
也就是说DexClassLoader用于存储opt优化后的dex文件的保存路径可以自己定义传进去,而PathClassLoader存储opt优化后的dex文件的保存路径是系统默认的。仅此这个区别而已。

双亲委托机制

类加载在加载类时,首先将加载任务委托给父类加载器加载,依次递归,如果父类加载器可以完成加载任务,就成功返回,如果父类加载器无法完成或者没有父类加载器时,才自己去加载。
简单一个词语概括:啃老族机制。

下面我们用一段代码来进行解释:


public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //PathClassLoader
        ClassLoader classLoader = this.getClassLoader();
        //BootClassLoader
        ClassLoader classLoader1 = Activity.class.getClassLoader();
        
        System.out.println("PathClassLoader getClassLoader:"+classLoader);
        System.out.println("PathClassLoader getClassLoader 的父亲 :"+classLoader.getParent());
        System.out.println("BootClassLoader Activity.class :"+classLoader1);

image.png

从打印中我们可以得出以下结论

  • MainActivity的类加载器是PathClassLoader
  • Activity的类加载器是PathClassLoader
  • PathClassLoader的父亲是BootClassLoader

注意:这里所说的父亲并不是指这个类的父类(BaseDexClassLoader),只是ClassLoder类里的一个类型为ClassLoader的成员变量名称叫做parent

image.png

image.png
那么为什么要使用双亲委托机制呢?

在我们应用程序启动的时候,BootClassLader会去加载FrameWork层的所有的类,例如上面的Activity,而我们自己写的代码BootClassLader是没办法去加载的,只能由PathClassLoader去加载,因为我们自己写的代码的类,在我们的apk里面,不在系统里面,例如上面的MainActivity。但现在我们写的MainActivity是派生于AppCompatActivity的,AppCompatActivity属于FrameWork层的类,已经被BootClassLader加载了,如果我们不使用双亲委托机制的话,PathClassLoader是不是加载不到FrameWork层的类,那是不是加载不了AppCompatActivity了,而运用双亲委托机制就能很好的让不同的类加载器去分别执行不同层级的类加载的任务,这样也是有利于系统的安全性,对于开发者来说你是无法知道怎么去加载系统的类的,你永远只能加载自己写的类,系统层面的类是不让你去加载的,只能由父亲BootClassLader去加载。

你可能感兴趣的:(Android类加载(二)——双亲委托机制)