欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android类加载器ClassLoader

程序员文章站 2022-07-03 14:28:53
...

简介        

        Android从5.0开始采用ART虚拟机,通过读取dex字节码来加载,相比Jvm通过读取class字节码来加载,是一种更为优化的方案, 可以将多个.class文件合并成一个classes.dex文件

类关系图

Android类加载器ClassLoader

Android的几种类加载器

 ClassLoader classLoader = MainActivity.class.getClassLoader();
        while (classLoader!=null) {
            Log.e(TAG, "test: " + classLoader.toString());
            //父类加载器,并不代表继承自父类加载器
            classLoader=classLoader.getParent();
        }


//运行结果
 E/MainActivity: test: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.example.component-ApyUv8YWIYq9y34yLqA8_A==/base.apk"],nativeLibraryDirectories=[/data/app/com.example.component-ApyUv8YWIYq9y34yLqA8_A==/lib/arm64, /system/lib64]]]
 E/MainActivity: test: aaa@qq.com

核心类

  1. PathClassLoader
  2. DexClassLoader
  3. BaseDexClassLoader
  4. ClassLoader
  5. BootClassLoader

在PathClassLoader和DexClassLoader之间,在API level 26之前,DexClassLoader可以直接加载优化后的dex文件存在的目录,在API level 26之后已经取消了。

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

    public BootClassLoader() {
        super(null);
    }
}

ClassLoader

public abstract class ClassLoader {

  private final ClassLoader parent;

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

    private ClassLoader(Void unused, ClassLoader parent) {
        this.parent = parent;
    }

    protected ClassLoader() {
        this(checkCreateClassLoader(), getSystemClassLoader());
    }
}
getSystemClassLoader获取的loader对应的是PathClassLoader

BaseDexClassLoader

public class BaseDexClassLoader extends ClassLoader {

   private final DexPathList pathList;

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

    }

BaseDexClassLoader构造函数, 有一个非常重要的过程, 那就是初始化DexPathList对象

该构造函数的参数说明:

  • dexPath: 包含目标类或资源的apk/jar列表;当有多个路径则采用:分割;
  • optimizedDirectory: 优化后dex文件存在的目录, 可以为null;
  • libraryPath: native库所在路径列表;当有多个路径则采用:分割;
  • ClassLoader:父类的类加载器.

DexPathList

public final class DexPathList {

    private Element[] dexElements;
   private static final String DEX_SUFFIX = ".dex";

  DexPathList(ClassLoader definingContext, String dexPath,
            String librarySearchPath, File optimizedDirectory, boolean isTrusted) {
      
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                           suppressedExceptions, definingContext, isTrusted);
  }

   private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
            List<IOException> suppressedExceptions, ClassLoader loader, boolean isTrusted) {
      Element[] elements = new Element[files.size()];
      int elementsPos = 0;
      /*
       * Open all files and load the (direct or contained) dex files up front.
       */
      for (File file : files) {
          if (file.isDirectory()) {
              // We support directories for looking up resources. Looking up resources in
              // directories is useful for running libcore tests.
              elements[elementsPos++] = new Element(file);
          } else if (file.isFile()) {
              String name = file.getName();

              DexFile dex = null;
              if (name.endsWith(DEX_SUFFIX)) {
                  // Raw dex file (not inside a zip/jar).
                  try {
                      dex = loadDexFile(file, optimizedDirectory, loader, elements);
                      if (dex != null) {
                          elements[elementsPos++] = new Element(dex, null);
                      }
                  } catch (IOException suppressed) {
                      System.logE("Unable to load dex file: " + file, suppressed);
                      suppressedExceptions.add(suppressed);
                  }
              } else {
                  try {
                      dex = loadDexFile(file, optimizedDirectory, loader, elements);
                  } catch (IOException suppressed) {
                      /*
                       * IOException might get thrown "legitimately" by the DexFile constructor if
                       * the zip file turns out to be resource-only (that is, no classes.dex file
                       * in it).
                       * Let dex == null and hang on to the exception to add to the tea-leaves for
                       * when findClass returns null.
                       */
                      suppressedExceptions.add(suppressed);
                  }

                  if (dex == null) {
                      elements[elementsPos++] = new Element(file);
                  } else {
                      elements[elementsPos++] = new Element(dex, file);
                  }
              }
              if (dex != null && isTrusted) {
                dex.setTrusted();
              }
          } else {
              System.logW("ClassLoader referenced unknown path: " + file);
          }
      }
      if (elementsPos != elements.length) {
          elements = Arrays.copyOf(elements, elementsPos);
      }
      return elements;
    }
}

dex文件封装到Element

loadClass

public abstract class ClassLoader {
       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) {
                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.
                    c = findClass(name);
                }
            }
            return c;
    }

 protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
}
  1. 获取当前类加载类是否已经加载过指定类,如果加载过,则直接返回
  2. 调用parent的类加载递归加载该类,检测是否加载,若已加载则直接返回,否则继续执行
  3. 直到没有parent时,调用findBootstrapClassOrNull,检查是否加载,若已加载直接返回,否则继续执行
  4. 在向上寻找parent类加载器的过程中,如果还是没有找到,则调用当前的类加载器,通过findClass加载

向下查看子类BaseDexClassLoader中是否有findClass方法

public class BaseDexClassLoader extends ClassLoader {

       private final DexPathList pathList;

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
       
        Class c = pathList.findClass(name, suppressedExceptions);
        
        return c;
    }
}

最终调用pathlist的findClass

public final class 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;
            }
        }
        return null;
    }
 }

最终调用Element的findClass

public final class DexPathList {
   
   static class Element {
     private final DexFile dexFile;

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

继续调用DexFile的loadClassBinaryName

public final class DexFile {

    public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
        return defineClass(name, loader, mCookie, this, suppressed);
    }
  
   private static Class defineClass(String name, ClassLoader loader, Object cookie,
                                     DexFile dexFile, List<Throwable> suppressed) {
        Class result = null;
        try {
            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;
    }

      private static native Class defineClassNative(String name, ClassLoader loader, Object cookie,
                                                  DexFile dexFile)
}

最终在native层创建目标类的对象并添加到虚拟机列表

总结

  • PathClassLoader: 主要用于系统和app的类加载器,其中optimizedDirectory为null, 采用默认目录/data/dalvik-cache/
  • BaseDexClassLoader: 比较基础的类加载器, PathClassLoader和DexClassLoader都只是在构造函数上对其简单封装而已.
  • BootClassLoader: 作为父类的类构造器。

 

热修复核心逻辑:在DexPathList.findClass()过程,一个Classloader可以包含多个dex文件,每个dex文件被封装到一个Element对象,这些Element对象排列成有序的数组dexElements。当查找某个类时,会遍历所有的dex文件,如果找到则直接返回,不再继续遍历dexElements。也就是说当两个类不同的dex中出现,会优先处理排在前面的dex文件,这便是热修复的核心精髓,将需要修复的类所打包的dex文件插入到dexElements前面。

相关标签: 架构师 热修复