ClassLoader屋

一、文件格式

1、class文件解析

        一种8位字节的二进制流文件

        

1.2、class文件弊端

  • 占用内存大,不适合移动端
  • 堆栈的加栈模式,加载速度慢
  • 文件IO操作多,类查找慢

 

2、dex文件解析

      一种8位字节的二进制流文件,各个数据按顺序紧密的排列,无间隙,整个应用中所有java源文件都在一个dex

          

 

二、java 类加载

名称加载目标说明
Bootstrap ClassLoader(BootstrapClassLoader)JAVA_HOME/jre/lib无法访问,是有c语言实现的
Extension ClassLoader(ExtClassLoader)JAVA_HOME/jre/lib/ext上级为Bootstrap,通过代码获取为null
Application ClassLoader(AppClassLoader)classpath上级为Extension
自定义加载类自定义上级为Applicaion1、/1、


 

三、Android加载器

1、类图

      

  解析:

(1)BootClassLoader源码在ClassLoader文件里,但是不是内部类

(2)PathClassLoader和DexClassLoader都继承BaseDexClassLoader

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);
    }
}
public class DexClassLoader extends BaseDexClassLoader {
  
    public DexClassLoader(String dexPath, String optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}

     在8.0(API26)以前区别就是一个参数:optimizedDirectory,表示生成的odex(优化的dex)存放的路径。

//版本8.0之前
public class BaseDexClassLoader extends ClassLoader {
     ...
   
    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    }
    ...
}


//8.0之后的版本
public class BaseDexClassLoader extends ClassLoader {
    ...
    
    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);

        if (reporter != null) {
            reporter.report(this.pathList.getDexPaths());
        }
    }
    ...
}


参数解析:

dexPath:dex或者文件jar的路径

optimizedDirectory:表示生成的odex(优化的dex)存放的路径

libraryPath:依赖so库的路径

 

四、双亲委托加载机制

1、图

      

代码如下:

protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
        Class<?> clazz = findLoadedClass(className);

        if (clazz == null) {
            ClassNotFoundException suppressed = null;
            try {
                clazz = parent.loadClass(className, false);
            } catch (ClassNotFoundException e) {
                suppressed = e;
            }

            if (clazz == null) {
                try {
                    clazz = findClass(className);
                } catch (ClassNotFoundException e) {
                    e.addSuppressed(suppressed);
                    throw e;
                }
            }
        }

        return clazz;
    }

解析:

(1)loadClass方法是定义在ClassLoader类里面的,因为子类都没有覆写这个方法,双亲委托机制必须的条件

(2)每个ClassLoader内部都有一个成员ClassLoader  parent,这个不是父类关系,类似上级classLoader,PathClassLoader的parent指向的是BootClassLoader,如果我们自定义DexClassLoader,那么在构造函数中传入的parent一般是PathClassLoader

(3)Framework层类用BootClassLoader加载,用户定义的类一般用PathClassLoader加载,包括谷歌集成的一些库,如AppCompatActivity也是由PathClassLoader加载的。

 

2、优点

  (1)防止被重复加载,保证字节码唯一

  (2)安全机制,防止核心类被篡改

 

3、自定义类加载器

  1. 继承ClassLoader父类
  2. 遵从双亲委派机制,重写findClass方法,不是重写loadClass方法
  3. 读取类文件的字节码
  4. 调用父类的defineClass方法来加载类
  5. 通过调用该类加载器的loadClass方法

 

5、BaseDexClassLoader解析

       

1、关键源码如下:

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

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

    ......
}

      成员变量pathList,它在构造函数中初始化。

2、DexPathList源码

final class DexPathList {
    
    ......

    private final Element[] dexElements;

    ......
    
    public DexPathList(ClassLoader definingContext, String dexPath,
            String libraryPath, File optimizedDirectory) {

        if (definingContext == null) {
            throw new NullPointerException("definingContext == null");
        }

        if (dexPath == null) {
            throw new NullPointerException("dexPath == null");
        }

        if (optimizedDirectory != null) {
            if (!optimizedDirectory.exists())  {
                throw new IllegalArgumentException(
                        "optimizedDirectory doesn't exist: "
                        + optimizedDirectory);
            }

            if (!(optimizedDirectory.canRead()
                            && optimizedDirectory.canWrite())) {
                throw new IllegalArgumentException(
                        "optimizedDirectory not readable/writable: "
                        + optimizedDirectory);
            }
        }

        this.definingContext = definingContext;

        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
        // save dexPath for BaseDexClassLoader
        this.dexElements = makePathElements(splitDexPath(dexPath), optimizedDirectory,
                                            suppressedExceptions);

       
        this.nativeLibraryDirectories = splitPaths(libraryPath, false);
        this.systemNativeLibraryDirectories =
                splitPaths(System.getProperty("java.library.path"), true);
        List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
        allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);

        this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories, null,
                                                          suppressedExceptions);

        if (suppressedExceptions.size() > 0) {
            this.dexElementsSuppressedExceptions =
                suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
        } else {
            dexElementsSuppressedExceptions = null;
        }
    }

  
    ......

    /**
     * Makes an array of dex/resource path elements, one per element of
     * the given array.
     */
    private static Element[] makePathElements(List<File> files, File optimizedDirectory,
                                              List<IOException> suppressedExceptions) {
        List<Element> elements = new ArrayList<>();
        /*
         * Open all files and load the (direct or contained) dex files
         * up front.
         */
        for (File file : files) {
            File zip = null;
            File dir = new File("");
            DexFile dex = null;
            String path = file.getPath();
            String name = file.getName();

            if (path.contains(zipSeparator)) {
                String split[] = path.split(zipSeparator, 2);
                zip = new File(split[0]);
                dir = new File(split[1]);
            } else if (file.isDirectory()) {
                // We support directories for looking up resources and native libraries.
                // Looking up resources in directories is useful for running libcore tests.
                elements.add(new Element(file, true, null, null));
            } else if (file.isFile()) {
                if (name.endsWith(DEX_SUFFIX)) {
                    // Raw dex file (not inside a zip/jar).
                    try {
                        dex = loadDexFile(file, optimizedDirectory);
                    } catch (IOException ex) {
                        System.logE("Unable to load dex file: " + file, ex);
                    }
                } else {
                    zip = file;

                    try {
                        dex = loadDexFile(file, optimizedDirectory);
                    } 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);
                    }
                }
            } else {
                System.logW("ClassLoader referenced unknown path: " + file);
            }

            if ((zip != null) || (dex != null)) {
                elements.add(new Element(dir, false, zip, dex));
            }
        }

        return elements.toArray(new Element[elements.size()]);
    }

}

       DexPathList的成员变量数组dexElements,元素是Element类型,Element是其内部类,数组的赋值是在makePathElements方法(这个方法随着版本不一样)。

3、总结

      3.1、谷歌的multiDex,腾讯的Tinker都是在操作dexElements这个变量。

       

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值