ClassLoader,了解一下

作者: 落魄的安卓开发 | 来源:发表于2018-06-11 16:56 被阅读18次

    ClassLoader 类图:

    图片来自文末参考文章中.jpg

    Android中ClassLoader的介绍

    1. ClassLoader

      介绍:

      ClassLoader是一个抽象类,其中定义了ClassLoader的主要功能。包括类加载,验证,卸载等

      构造方法:

       public abstract class ClassLoader {
           private ClassLoader parent;
       
           protected ClassLoader() {
               this(getSystemClassLoader(), false);
           }
       
           protected ClassLoader(ClassLoader parentLoader) {
               this(parentLoader, false);
           }
       
           ClassLoader(ClassLoader parentLoader, boolean nullAllowed) {
               if (parentLoader == null && !nullAllowed) {
                   //父类的类加载器为空,则抛出异常
                   throw new NullPointerException("parentLoader == null && !nullAllowed");
               }
               parent = parentLoader;
           }
       }
      
    2. BootClassLoader

      介绍:

      • 父类构造器

      • BootClassLoader是ClassLoader的内部类,是单例类,包内可见,我们没法调用,也不能使用它来动态加载。Android系统启动时会使用BootClassLoader来预加载常用类。

      构造方法:

       class BootClassLoader extends ClassLoader {
           private static BootClassLoader instance;
       
           public static synchronized BootClassLoader getInstance() {
               if (instance == null) {
                   instance = new BootClassLoader();
               }
       
               return instance;
           }
       
           public BootClassLoader() {
               super(null, true);
           } 
       }
      
    3. BaseDexClassLoader

      介绍:

      • BaseDexClassLoader继承自ClassLoader,是抽象类ClassLoader的具体实现类

      • PathClassLoader和DexClassLoader都继承它

      • 在它内部会初始化DexPathList对象,DexPathList这个对象中存储了dexElements(记录所有的dexFile文件)和nativeLibraryPathElements(记录所有的Native动态库, 包括app目录的native库和系统目录的native库)

      构造方法:

        public class BaseDexClassLoader extends ClassLoader {
            private final DexPathList pathList;
        
            public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) {
                super(parent);
                this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
            }
        }   
    

    参数:

    • dexPath: 包含目标类或资源的apk/jar列表;当有多个路径则采用:分割;

    • optimizedDirectory: 优化后的dex文件存在的目录, 可以为null;

    • libraryPath: native库所在路径列表;当有多个路径则采用:分割;

    • ClassLoader:父类的类加载器.

    1. PathClassLoader

      介绍:

      • 继承于BaseDexClassLoader. 只是封装了一下构造函数,没有复写父类的任何方法,所以具体的实现都在BaseDexClassLoader中

      • 主要用于系统和app的类加载器,其中optimizedDirectory为null, 采用默认目录/data/dalvik-cache/

      • dexPath 比较受限制,一般是已经安装应用的apk文件路径。在Android中,App安装到手机后,apk里面的classes.dex中的class均是通过PathClassLoader来加载的

      • BootClassLoader 是 PathClassLoader 的父加载器

      构造方法:

       public class PathClassLoader extends BaseDexClassLoader {
      
           public PathClassLoader(String dexPath, ClassLoader parent) {
               super(dexPath, null, null, parent);
           }
       
           public PathClassLoader(String dexPath, String libraryPath,
                   ClassLoader parent) {
               super(dexPath, null, libraryPath, parent);
           }
       }
      
    2. DexClassLoader

    介绍:

    • 封装了BaseDexClassLoader对象,并没有覆写父类的任何方法

    • 可以从包含classes.dex的jar或者apk中,加载类的类加载器, 可用于执行动态加载

    • 构造方法中,参数optimizedDirectory用来缓存优化的dex文件的路径,即从apk或jar文件中提取出来的 dex 文件。该路径不可以为空,且应该是应用私有的,有读写权限的路径

    构造方法:

        public class DexClassLoader extends BaseDexClassLoader {
    
            public DexClassLoader(String dexPath, String optimizedDirectory,
                    String libraryPath, ClassLoader parent) {
                super(dexPath, new File(optimizedDirectory), libraryPath, parent);
            }
        }
    

    双亲委托机制

    类加载器查找Class所采用的是双亲委托模式,所谓双亲委托模式就是首先判断该Class是否已经加载,如果没有则不是自身去查找而是委托给父加载器进行查找,这样依次的进行递归,直到委托到最顶层的Bootstrap ClassLoader,如果Bootstrap ClassLoader找到了该Class,就会直接返回,如果没找到,则继续依次向下查找,如果还没找到则最后会交由自身去查找。

    具体委托过程如下:

    1. 源 ClassLoader 先判断该 Class 是否已加载,如果已加载,则直接返回 Class,如果没有则委托给父类加载器。

    2. 父类加载器判断是否加载过该 Class,如果已加载,则直接返回 Class,如果没有则委托给祖父类加载器。

    3. 依此类推,直到始祖类加载器(引用类加载器)。

    4. 始祖类加载器判断是否加载过该 Class,如果已加载,则直接返回 Class,如果没有则尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,则委托给始祖类加载器的子类加载器。

    5. 始祖类加载器的子类加载器尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,则委托给始祖类加载器的孙类加载器。

    6. 依此类推,直到源 ClassLoader。

    7. 源 ClassLoader 尝试从其对应的类路径下寻找 class 字节码文件并载入。如果载入成功,则直接返回 Class,如果载入失败,源 ClassLoader 不会再委托其子类加载器,而是抛出异常。

    类加载

    1. loadClass(className) 调用加载class的方法。

      在loadClass()内部会先判断是否已经加载过该class,如果没有加载过就调用父类的loadClass(),如果父类中没有加载到就调用自己的findClass(className)方法去自己的路径中加载该class

    2. findClass(className)

      1. findClass()方法内部中是调用pathList(pathList是在BaseDexClassLoader构造方法中初始化的DexPathList对象)的findClass(name...)

      2. 即调用DexPathList内部的findClass(name) 。在该方法中会遍历dexEldments中的dex文件,然后调用dex.loadClassBinaryName方法去查找该类。

      3. 即调用DexFile内部的,loadClassBinaryName方法,在该方法中调用defineClass()方法

      4. defineClassNative()这是native方法,在该native方法中,去找对应的类

    loadClass源码流程参考

    从源码分析 Android dexClassLoader 加载机制原理

    ClassLoader在热修复中的应用

    在前面类加载过程中分析说了,类加载机制是双亲委托机制,而且在loadClass过程中会遍历pathList(dexElements)中的dex文件,所以当在前面的dex文件中找到类就直接返回了,而不会再进行查找,所以可以通过将修复的dex插到dexElements前面即可。思路是这样的,具体的操作会复杂的多。

    ClassLoader动态加载Jar实践

    1. 生成jar包my.jar

       package com.thc.myjar;
      
       public class Plugin {
       
           public String getPluginStr() {
               return "恭喜你拿到了插件中的内容";
           }
       
           public int addInPulgin(int a, int b) {
               return a + b;
           }
       
       }
      
    2. 使用dx.jar将jar包转换成dex化之后的jar包

       dx --dex --output=mydex.jar my.jar
      
    3. push到或者代码复制到sdcard目录,然后通过DexClassLoader来进行加载

        private void loadJar() {
            /**
             * dexPath : dexPath
             *
             */
            String absolutePath = getCacheDir().getAbsolutePath();
            DexClassLoader dexClassLoader = new DexClassLoader(
                    "/data/data/com.thc.pluginsample/cache/" + jarName,
                    absolutePath,
                    null,
                    getClassLoader());
            try {
                Class<?> aClass = dexClassLoader.loadClass("com.thc.myjar.Plugin");
                Object o = aClass.newInstance();
                Method getPluginStr = aClass.getMethod("getPluginStr");
                String msg = (String) getPluginStr.invoke(o);
                Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    

    动态加载Jar Demo地址

    感谢巨人的肩膀:

    gityuan

    热修复入门:Android 中的 ClassLoader

    ClassLoader

    相关文章

      网友评论

        本文标题:ClassLoader,了解一下

        本文链接:https://www.haomeiwen.com/subject/uouheftx.html