• Android随笔-ClassLoader


    概述

    任何一个Java程序都有一个或多个class,程序运行时,需要将class文件加载到JVM中才可以使用,负责加载class文件的就是ClassLoader。每一个Class对象内部都有一个classLoader字段来标记该对象应该由哪一个ClassLoader加载。

    public final class Class<T> implements java.io.Serializable,
                                  GenericDeclaration,
                                  Type,
                                  AnnotatedElement {
        ...
        /** defining class loader, or null for the "bootstrap" system loader. */
        private transient ClassLoader classLoader;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    代码中的ClassLoader就是本文要概述的类加载器

    分类

    ClassLoader是抽象类,Android使用最多的ClassLoader实现类有三种:BootClassLoader,PathClassLoader,DexClassLoader。

    public abstract class ClassLoader {
    
        static private class SystemClassLoader {
            public static ClassLoader loader = ClassLoader.createSystemClassLoader();
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • BootClassLoader
        private static class BootClassLoader extends BuiltinClassLoader {
            BootClassLoader(URLClassPath bcp) {
                super((String)null, (BuiltinClassLoader)null, bcp);
            }
    
            protected Class<?> loadClassOrNull(String cn) {
                return ClassLoaders.JLA.findBootstrapClassOrNull(this, cn);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    加载Android Framework中的class文件。
    测试:

     Log.d(TAG, "Activity的ClassLoader:" + Activity.class.getClassLoader().toString());
    
    • 1

    结果:

    ActivityClassLoaderjava.lang.BootClassLoader@93afdaf
    
    • 1
    • PathClassLoader
    public class PathClassLoader extends BaseDexClassLoader {
        public PathClassLoader(String dexPath, ClassLoader parent) {
            super((String)null, (File)null, (String)null, (ClassLoader)null);
            throw new RuntimeException("Stub!");
        }
    
        public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
            super((String)null, (File)null, (String)null, (ClassLoader)null);
            throw new RuntimeException("Stub!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    加载Android应用程序类,可以指定dex,以及jar、zip、apk中的classes.dex文件。
    测试:

          Log.d(TAG, "AppCompatActivity的ClassLoader:" + AppCompatActivity.class.getClassLoader().toString());
    
    • 1

    结果:

    AppCompatActivityClassLoaderdalvik.system.PathClassLoader[DexPathList[
    
    • 1

    说明AppCompatActivity并不属于Framework。

    • DexClassLoader
    public class DexClassLoader extends BaseDexClassLoader {
        public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
            super((String)null, (File)null, (String)null, (ClassLoader)null);
            throw new RuntimeException("Stub!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    optimizedDirectory:dexopt的优化文件odex的产出目录,为null时的默认路径为:/data/dalvik-cache。
    DexClassLoader加载指定的dex,以及jar、zip、apk中的classes.dex文件,多用于热修复和插件化。

    联系

    PathClassLoader和DexClassLoader都继承自BaseDexClassLoader。两者的唯一区别是DexClassLoader多个一个optimizedDirectory参数,并且会将其创建的File对象传递给super。两者都可以加载指定dex,以及jar、zip、apk中的classes.dex文件。
    在这里插入图片描述

    流程

    在这里插入图片描述

    1. 创建DexPathList 对象pathList,同时将dex文件加载到内存中,生成DexFile对象,再生成Element,并添加到Element数组dexElements。
      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();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    1. BaseDexClassLoader调用findClass方法。
     @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
            Class c = pathList.findClass(name, suppressedExceptions);
            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;
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    1. 调用DexPathList 的findClass()方法。
    public Class<?> findClass(String name, List<Throwable> suppressed) {
            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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
        public Class<?> findClass(String name, ClassLoader definingContext,
                    List<Throwable> suppressed) {
                return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                        : null;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    遍历dexElements里面的元素,通过遍历所有加载过的dex文件,调用loadClassBinaryName方法寻找想要的类,如果有就直接返回。

    双亲委托机制

        protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
        {
                // 首先,检查class是否已被加载过
                Class<?> c = findLoadedClass(name);
                if (c == null) {
                    try {
                        if (parent != null) {
                        	// 如果父类已加载,则使用父类进行加载
                            c = parent.loadClass(name, false);
                        } else {
                            // 如果父类为null,则使用findBootstrapClassOrNull进行加载
                            c = findBootstrapClassOrNull(name);
                        }
                    } catch (ClassNotFoundException e) {
                    }
                    if (c == null) {
                        // 如果没有发现,就自己findclass
                        c = findClass(name);
                    }
                }
                return c;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    如果父类为null时,其实使用findBootstrapClassOrNull加载其实相当于什么也没做。

        private Class<?> findBootstrapClassOrNull(String name)
        {
            return null;
        }
    
    • 1
    • 2
    • 3
    • 4

    类加载器在加载类时候,会采用双亲委托机制。首先将加载任务委托给父类进行加载,若有多级父类,则一次递归,若父类可以完成加载,则成功返回;如果父类没有办法完成加载任务就自己加载。

  • 相关阅读:
    传言称 iPhone 16 Pro 将支持 40W 快速充电和 20W MagSafe
    [哈希算法]----判断图片正反
    主流接口测试框架对比
    第二章:并行硬件和并行软件
    基于javaweb的在线化妆品商城系统(java+ssm+jsp+js+bootstrap+mysql)
    直播带货系统,乡村直播电商平台的新选择
    Power BI 傻瓜入门 10. 完善数据模型
    面试官猛的一问:Spring的Bean注入如何解决循环依赖的?
    PFC(Power Factor Correction)功率因数校正电路
    CSDN每日一练 |『买苹果』『最长回文串』『查找整数』2023-10-20
  • 原文地址:https://blog.csdn.net/qq_34202054/article/details/126636203