Android 中的ClassLoader

概述

上一篇文章咱们了解了JavaClassLoader,上一篇文章传送门JVM 类加载机制java

其实Android中的ClassLoaderjava中的是不同的,由于java中的CalssLoader主要加载Class文件,可是Android中的ClassLoader主要加载dex文件数组

Android中的ClassLoader

Android中的ClassLoader分为俩种类型,系统类加载器自定义类加载器。其中系统的类加载器分为三种,BootClassLoaderPathClassLoaderDexClassLoader安全

BootClassLoader

Android 系统启动时,会用BootClassLoader来预加载经常使用类,与java中的ClassLoader不一样,他不是用C++实现,而是用java实现的,以下:cookie

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;
    }
...
复制代码

BootClassLoaderClassLoader的内部类,并继承自ClassLoaderBootClassLoader是一个单例类,须要注意的是BootClassLoader是默认修饰符,只能包内访问,咱们是没法使用的app

DexClassLoader

DexClassLoader能够加载dex文件和包含dex文件的压缩文件(好比jar和apk文件),无论加载那种文件,最终都是加载dex文件,咱们看一下代码ide

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!");
    }
}
复制代码

DexClassLoader有四个参数post

  • dexPath:dex相关文件的路径集合,多个文件用路径分割符分割,默认的文件分割符为 ":"
  • optimizedDirectory:解压的dex文件储存的路径,这个路径必须是一个内部储存路径,通常状况下使用当钱应用程序的私有路径/data/data/<Package Name>/...
  • librarySearchPath:包含C++库的路径集合,多个路径用文件分割符分割,能够为null
  • parent:父加载器

DexClassLoade继承自BaseDexClassLoader,全部的实现都是在BaseDexClassLoaderthis

PathClassLoader

AndroidPathClassLoader来加载系统类和应用程序类,代码以下:spa

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!");
    }
}
复制代码

PathClassLoader继承自BaseDexClassLoader,全部的实现都是在BaseDexClassLoadercode

PathClassLoader构造方法没有optimizedDirectory参数,由于PathClassLoader默认optimizedDirectory参数是/data/dalvik-cache,很显然PathClassLoader没法定义解压的dex储存的位置,所以PathClassLoader一般用来加载已经安装的apkdex文件(安装的apk的dex文件在/data/dalvik-cache中)

ClassLoder的继承关系

运行一个应用程序须要几个ClassLoader呢?

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ClassLoader classLoader = MainActivity.class.getClassLoader();
        while (classLoader != null) {
            Log.d("mmmClassLoader", classLoader.toString()+"\n");
            classLoader = classLoader.getParent();
        }

    }
复制代码

看下log

mmmClassLoader: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.baidu.bpit.aibaidu.idl3-2/base.apk"],
nativeLibraryDirectories=[/data/app/com.baidu.bpit.aibaidu.idl3-2/lib/arm64, /data/app/com.baidu.bpit.aibaidu.idl3-2/base.apk!/lib/arm64-v8a, /system/lib64, /vendor/lib64, /system/vendor/lib64, /product/lib64]]]
 
java.lang.BootClassLoader@fcb14c9
复制代码

咱们看到主要用了俩个ClassLoader,分别是PathClassLoaderBootClassLoader,其中DexPathList包含了不少apk的路径,其中/data/app/com.baidu.bpit.aibaidu.idl3-2/base.apk就是实例应用安装在手机上的位置,DexPathList是在BaseDexClassLoder中建立的,里面储存dex相关文件的路径

除了上方的3中ClassLoader,Android还提供了其余的类加载器和ClassLoader相关类,继承关系以下:

分别介绍一下上方的8种ClassLoader

  • ClassLoader是一个抽象类,其中定义了ClassLoder的主要功能,BootClassLoader是他的内部类
  • SecureClassLoader和JDK8中的SecureClassLoader代码是同样的,他继承抽象类ClassLoaderSecureClassLoader并非ClassLoader的实现类,而是扩展了ClassLoader权限方面的功能,增强了Classloader的安全性
  • URLClassLoader和JDK8中的URLClassLoader是同样的,他继承了SecureClassLoader通能过URL路径从jar文件中加载类和资源
  • InMemoryDexClassLoader他是Android8.0新加的类加载器,继承自BaseDexClassLoader,用于加载内存中的dex文件
  • BaseDexClassLoader继承自ClassLoader,是抽象类ClassLoader的具体实现类

ClassLoader的加载过程

Android中的ClassLoader一样遵循双亲委托模型,ClassLoader的加载方法为loadClass,方法定义在ClassLoader中

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException{
            // 首先检查类是否被加载过
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    //若是父类抛出ClassNotFoundException异常
                    //则说明父类不能加载该类
                }

                if (c == null) {
                    //若是父类没法加载,则调用自身的findClass进行加
                    c = findClass(name);
                }
            }
            return c;
    }
复制代码

上方逻辑很清楚,首先检查类是否被加载过,若是没有被加载过,就调用父类的加载器加载,若是父类加载器为空就调用启动加载器加载,若是父类加载失败,就调用本身的findClass加载,咱们看一下这个方法

protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }
复制代码

直接抛出异常,这说明findClass须要子类BaseDexClassLoader实现,以下:

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());
        }
    }
    
    ...
    
      @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
        //注释1
        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;
    }
复制代码

首先在构造方法内建立了DexPathList,而后再注释1处调用DexPathListfindClass方法

public Class<?> findClass(String name, List<Throwable> suppressed) {      //注释1
        for (Element element : dexElements) {
            //注释2
            Class<?> clazz = element.findClass(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }

        if (dexElementsSuppressedExceptions != null) {
            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
        }
        return null;
    }
复制代码

在注释1处遍历dexElements数组,在注释2处调用ElementfindClass方法

public Element(File dir, boolean isDirectory, File zip, DexFile dexFile) {
            System.err.println("Warning: Using deprecated Element constructor. Do not use internal"
                    + " APIs, this constructor will be removed in the future.");
            if (dir != null && (zip != null || dexFile != null)) {
                throw new IllegalArgumentException("Using dir and zip|dexFile no longer"
                        + " supported.");
            }
            if (isDirectory && (zip != null || dexFile != null)) {
                throw new IllegalArgumentException("Unsupported argument combination.");
            }
            if (dir != null) {
                this.path = dir;
                this.dexFile = null;
            } else {
                this.path = zip;
                this.dexFile = dexFile;
            }
        }
        ...
        
        
            public Class<?> findClass(String name, ClassLoader definingContext,
                List<Throwable> suppressed) {
            //注释1
            return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                    : null;
        }
复制代码

咱们从构造方法能够看出,它内部封装了DexFile,他用于加载dex,注释1处若是dexFile不为null则调用DexFileloadClassBinaryName方法

public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
        return defineClass(name, loader, mCookie, this, suppressed);
    }
复制代码

又调用了defineClass方法

private static Class defineClass(String name, ClassLoader loader, Object cookie, DexFile dexFile, List<Throwable> suppressed) {
        Class result = null;
        try {
            //注释1
            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;
    }
复制代码

注释1处调用了defineClassNative方法来加载dex相关文件,这个是native方法不在向下分析

参考: 《Android进阶解密》

相关文章
相关标签/搜索