Android Class类加载机制

1 前言

Class类加载机制是了解Android高级技术的基础,现在热门的热修复,插件化,以及Dex分包等技术,都需要理解class类加载机制,了解了class类加载机制我们能更好的理解Android内部原理,从而使我们的技术提高一个层次
Android是基于java的,我们先来了解一下java的类加载机制

2 java Class类加载机制

java系统自带有三个类加载器,分别是Bootstrap ClassLoader, Extention ClassLoader,Appclass Loader
Bootstrap ClassLoader :是用C++语言实现的,最顶层的加载类,主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。我们可以打开我的电脑,在上面的目录下查看,看看这些jar包是不是存在于这个目录。
Extention ClassLoader :扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。
Appclass Loader:也称为SystemAppClass 加载当前应用的classpath的所有类。

加载顺序
1. Bootstrap CLassloder
2. Extention ClassLoader
3. AppClassLoader

在java代码中我们可以使用如下代码进行测试:

ClassLoader classLoader = Main.class.getClassLoader();
//是APPClassLoader
System.out.println("Main.class ClassLoader:" + classLoader.toString());
URL[] urls = ((URLClassLoader)classLoader).getURLs();
printURL(urls);

ClassLoader parent1 = classLoader.getParent();
//是ExtClassLoader
System.out.println("Main.class parent ClassLoader:" + parent1.toString());

URL[] urls2 = ((URLClassLoader)parent1).getURLs();
printURL(urls2);

ClassLoader parent2 = parent1.getParent();
//是BootstrapLoader
System.out.println("ExtClassLoader parent ClassLoader:" + parent2.toString());

这三者的关系如下:
这里写图片描述

这三者的继承关系如下:
这里写图片描述

3 Android Class类加载机制

在Android中 有 2种类加载器:
PathClassLoaderDexClassLoader。分别位于如下
libcore\dalvik\src\main\java\dalvik\system\PathClassLoader.java
libcore\dalvik\src\main\java\dalvik\system\DexClassLoader.java

定义如下

/**
 * Provides a simple {@link ClassLoader} implementation that operates on a list
 * of files and directories in the local file system, but does not attempt to
 * load classes from the network. Android uses this class for its system class
 * loader and for its application class loader(s).
 */
public class **PathClassLoader** extends BaseDexClassLoader {
    ......
}


/**
 * A class loader that loads classes from {@code .jar} and {@code .apk} files
 * containing a {@code classes.dex} entry. This can be used to execute code not
 * installed as part of an application.
 *
 * <p>This class loader requires an application-private, writable directory to
 * cache optimized classes. Use {@code Context.getCodeCacheDir()} to create
 * such a directory: <pre>   {@code
 *   File dexOutputDir = context.getCodeCacheDir();
 * }</pre>
 *
 * <p><strong>Do not cache optimized classes on external storage.</strong>
 * External storage does not provide access controls necessary to protect your
 * application from code injection attacks.
 */
public class **DexClassLoader** extends BaseDexClassLoader {
    ......
}

可以看到都继承了BaseDexClassLoader 这个基类,这个类又是继承ClassLoader的,这里先不去管它。我们来看注释。
对于PathClassLoader,从文档上的注释来看:Android是使用这个类作为其系统类和应用类的加载器。并且对于这个类呢,只能去加载已经安装到Android系统中的apk文件。
对于DexClassLoader,依然看下注释:可以看到可以加载从jar包中的,apk中的类。

Android要加载一个类 是通过ClassLoader的findClass方法 在dex中查找这个类 找到后加载到内存
我们来看下Android中类是如何加载的。
我们查看PathClassLoader与DexClassLoader均发现没有findClass()方法,因此我们在其父类BaseDexClassLoader中找一下

/**
 * Base class for common functionality between various dex-based
 * {@link ClassLoader} implementations.
 */
public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;

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

    ....
}

可以看到,Android中类的加载是从DexPathList 的findClass()方法中加载的,我们再来看DexPathList 中查看

final class DexPathList {
    private static final String DEX_SUFFIX = ".dex";

    ......

    /**
     * List of dex/resource (class path) elements.
     * Should be called pathElements, but the Facebook app uses reflection
     * to modify 'dexElements' (http://b/7726934).
     */
    private final Element[] dexElements;

    ......

    /**
     * Finds the named class in one of the dex files pointed at by
     * this instance. This will find the one in the earliest listed
     * path element. If the class is found but has not yet been
     * defined, then this method will define it in the defining
     * context that this instance was constructed with.
     *
     * @param name of class to find
     * @param suppressed exceptions encountered whilst finding the class
     * @return the named class or {@code null} if the class is not
     * found in any of the dex files
     */
    public Class findClass(String name, List<Throwable> suppressed) {
        for (Element element : dexElements) {
            DexFile dex = element.dexFile;

            if (dex != null) {
                Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
                if (clazz != null) {
                    return clazz;
                }
            }
        }
        if (dexElementsSuppressedExceptions != null) {
            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
        }
        return null;
    }
}

可以看到查找类是在dexElements数组中依次遍历查找的。一个classloader可以包含多个dex,其中这个集合中的对象就是所有的dex文件,查找是从头开始遍历所有的dex 如果在dex中找到所需要的类,那么就直接返回。
在这个dex中查找相应名字的类,之后 defineClass把字节码交给虚拟机就完成了类的加载。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值