Android ClassLoader 机制分析

1.Java ClassLoader机制

Android 的ClassLoader机制与Java 的ClassLoader机制略有不同,主要差异在Java加载的是class文件,而Android 加载的是dex文件,但是在ClassLoader的设计体系和思想上还是相近的,毕竟Android 的虚拟机也是从JVM改变演进的,所以我们先来跟踪分析java 的ClassLoader机制,对于我们理解Android 上面的ClassLoader机制会有更好的体会,也更好的区分二者的差异。

1.1 JVM类加载机制

什么是类加载机制,是虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制。

这个过程包括以下几个阶段,加载,验证,准备,解析,初始化,使用,卸载。

而加载阶段实际就是通过一个类的名字获取定义这个类的二进制字节流,然后把字节流中代表的静态存储结构转换为方法区的运行时数据结构。同时在内存中生成一个代表这个类的对象,作为方法区这个类的各种数据的访问入口,下图是虚拟机类加载的一个简单示例图,而加载的执行我们是通过类加载器实现,也就是ClassLoader。

Android ClassLoader 机制分析

1.2 Java ClassLoader

java 的类加载器主要涉及Bootstrap ClassLoader,ExtClassLoader,AppClassLoader这三个加载器,他们的加载层级不同。下面是Java的ClassLoader相关类的关系图

Android ClassLoader 机制分析

这个类图不包含Bootstrap ClassLoader ,因为它是C++代码实现的加载器。下面主要简单介绍下这几个加载器。

 

1.2.1 Bootstrap ClassLoader

启动类加载器,它主要用来把<JAVA_HOME>\lib目录中的,或者被-XbootclassPath参数所指定的路径的类库进行加载,这些类库主要都是java的一些系统类,像java.lang*,java.util.*等下面是通过代码打印出的加载home路径的类库文件。

package checkClassLoader;

public class Test {

         public static void main(String[] args) {

                   System.out.println(System.getProperty("sun.boot.class.path"));

         }

}

/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/resources.jar:

/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar:

/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/sunrsasign.jar:

/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jsse.jar:

/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jce.jar:

/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/charsets.jar:

/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jfr.jar:

/usr/lib/jvm/java-8-openjdk-amd64/jre/classes

 

1.2.2 ExtClassLoader

从上面类图可以看到它的继承关系,它的祖类是ClassLoader,那么它的父类和祖父类分别URLClassLoader和SecureClassLoader。

ClassLoader是一个抽象类,基本类加载器的核心处理都在这里。

SecureClassLoader继承了ClassLoader,引入了安全管理器,增加了权限方面的功能。

URLClassLoader继承SecureClassLoader,通过URI路径加载类库资源

ExtClassLoader 它和AppClassLoader都是继承自URLClassLoader,其中ExtClassLoader是扩展类加载器,主要用来加载<JAVA_HOME>\lib\ext目录或者java.ext.dirs 系统变量所指定的路径的所有类库,开发者是可以直接使用的。

我PC本地的路径:

System.out.println(System.getProperty("java.ext.dirs"));

/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext:

/usr/java/packages/lib/ext

1.2.3 AppClassLoader

应用程序类加载器,它主要负责加载当前应用程序的ClassPath上的所有类库,如果没有自定义个类加载器,一般应用程序就使用它。它通过ClassLoader.getSystemClassLoader()方法获取,所以也可以叫他系统类加载器。

1.2.4 自定义加载器

自定义加载器就是我们在我们的代码里面覆写ClassLoader,并重新实现findClass()方法,返回Class对象。

这样我们可以加载指定路径或者网络上文件的jar包或者class文件。这增加了java加载类的动态性。

但是这里需要说明一个问题,就是不同的类加载器如果加载同一个Class文件,获取到的两个类是不相等的,例如使用instanceof进程对比,返回false。为什么会这样呢,因为对于任意一个类,都需要由加载它的类加载器和这个类本身确立其在JVM的唯一性,类加载器在JVM是有独立的类名称空间的。

这里我写了个demo去加载自定义路径的class文件

package checkNull;

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

 

public class CheckClassLoader {

        

public static void main(String[] args) {

         String fileName = "/home/local/SPREADTRUM/jamous.zhang/Log";

         MyClassLoader classLoader = new MyClassLoader(fileName);

         try {

                   Class c = classLoader.loadClass("com.example.Test");

                   if(c != null) {

                            try {

                                     Object obj = c.newInstance();

                            } catch (InstantiationException | IllegalAccessException e) {

                                     // TODO Auto-generated catch block

                                     e.printStackTrace();

                            }

                   }

         } catch (ClassNotFoundException e) {

                   // TODO Auto-generated catch block

                   e.printStackTrace();

         }

}

 

}

 

package checkNull;

 

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

 

class MyClassLoader extends ClassLoader{

    private String path;

 

    public MyClassLoader(String path){

        this.path = path;

    }

 

    @Override

    protected Class<?> findClass(String name) throws ClassNotFoundException {

        Class clazz = null;

        byte[] data = loadFileClass(name);

        System.out.println(data.length);

        if (data != null) {

            clazz = defineClass(name,data,0,data.length);

            return clazz;

        }

        return super.findClass(name);

    }

 

    private byte[] loadFileClass(String name){

        String fileName = getFileName(name);

        File file = new File(path,fileName);

        System.out.println(file.toString());

        InputStream in = null;

        ByteArrayOutputStream out = null;

        try {

            in = new FileInputStream(file);

            out = new ByteArrayOutputStream();

            byte[] buffer = new byte[1024];

            int length;

            while ((length = in.read(buffer)) > 0) {

                System.out.println(length);

                out.write(buffer,0,length);

            }

            return out.toByteArray();

        } catch (IOException e){

            e.printStackTrace();

        } finally {

            try {

                if(in != null){

                    in.close();

                }

            } catch (IOException e){

                e.printStackTrace();

            }

            try {

                if(out != null){

                    out.close();

                }

            } catch (IOException e){

                e.printStackTrace();

            }

        }

        return null;

    }

 

    private String getFileName(String name){

        int index = name.lastIndexOf(".");

        if(index == -1) {

            return name+".class";

        } else {

           System.out.println(name.substring(index+1)+".class");

            return name.substring(index+1)+".class";

        }

    }

}

1.3 双亲委派机制

双亲委派机制可以说是ClassLoader的重点特性,这个在Android上也是有相同特性的,

首先我们看下几个类加载器的层次

Android ClassLoader 机制分析

除了BootStrap ClassLoader 其他的类加载器都是有自己父加载器,而双亲委派机制就是说,一个类加载器收到加载类的请求,当然前提是这个类没有被它以及它的父层加载器加载过,并存在内存的情况。它自己不会先加载,而且委托给自己的父加载器去完成,并逐层上传请求,一直穿到顶层类加载器,如果父加载器无法处理这个加载请求,再传递给子加载器,让他自己去加载。

 

优势:(1)这种机制保障了系统的核心类库只会由顶层加载器加载,即使你写了跟系统类库相同名字的类,也不会得到加载。

(2)避免重复加载

 

这里贴了下Android ClassLoader的代码,跟java的一样

    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;

    }

 

 

2. Android ClassLoader机制

上一章我们介绍了Java的ClassLoader的机制和一些原理,这一章我们介绍下Android 的ClassLoader,着重会说下差异。

首先Android ClassLoader 加载的不再是class文件而是dex文件,所以相关ClassLoader也做了改变,下图是Android ClassLoader相关类的关系

Android ClassLoader 机制分析

这里面我们把类加载器分为三类,BootClassLoader,PatchClassLoader,DexClassLoader

其实PatchClassLoader,DexClassLoader只是对于BaseDexClassLoader的封装,具体加载逻辑是相同。

而DelegateLastClassLoader则是对PathClassLoader的封装。

那么我们具体分别说下这三个类

2.1 BootClassLoader

继承ClassLoader,它类似java中的BootStrap ClassLoader,主要是加载Framework的核心类库。它是在zygote启动阶段preload class操作开始工作的,主要负责加载/system/etc/preloaded-classes 中的相关类。这样使得zygote进程的内存区域load了Framework的基本类,保证了后续通过zygote fork的新进程(应用进程),因为复制父进程的资源,而默认加载了相关Framework的class,避免后续的重复加载。下面我们通过代码来看下相关流程。

/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";

    private static void preloadClasses() {

        final VMRuntime runtime = VMRuntime.getRuntime();

 

        InputStream is;

        try {//通过文件字节流打开文件preloaded-classes

            is = new FileInputStream(PRELOADED_CLASSES);

        }

                   ......

 

        try {

            BufferedReader br

                = new BufferedReader(new InputStreamReader(is), 256);//转换为字符流进行读取

 

            int count = 0;

            String line;

            while ((line = br.readLine()) != null) {

                // 逐行读取,并去除无效行

                line = line.trim();

                if (line.startsWith("#") || line.equals("")) {

                    continue;

                }

                try {//然后调用Class.forName 进行加载类

                    Class.forName(line, true, null);

                    count++;

preloadClass方法,主要通过字符流去读取文件preload-classed中每行的类名,然后调用Class.forName进行实际的加载。

 

/libcore/ojluni/src/main/java/java/lang/Class.java

传入三个参数,

Name 类的名字 –》line

Initialize 初始化标识值 ->这个为true,则需要在load dex加载类到内存后,要做初始化操作,初始化过程主要是根据程序的主观要求去初始化相关的变量和资源。

Loader 加载器 –》这里为null,代表使用bootClassLoader.

public static Class<?> forName(String name, boolean initialize,

                                                                    ClassLoader loader)

         throws ClassNotFoundException

{

         if (loader == null) {//没有指定加载器,则获取BootClassLoader的单例,作为本次类加载的加载器

                   loader = BootClassLoader.getInstance();

         }

         Class<?> result;

         try {//交付给native 的ClassForName 方法进行类加载操作

                   result = classForName(name, initialize, loader);

         } catch (ClassNotFoundException e) {

                   Throwable cause = e.getCause();

                   if (cause instanceof LinkageError) {

                            throw (LinkageError) cause;

                   }

                   throw e;

         }

         return result;

}

Class的forname主要做了两个事情,1.没有指定加载器的交给BootClassLoader加载器加载 2.通过调用classForName把加载任务交付给native层执行

/art/runtime/native/java_lang_Class.cc

static jclass Class_classForName(JNIEnv* env, jclass, jstring javaName, jboolean initialize,

                                 jobject javaLoader) {

  ScopedFastNativeObjectAccess soa(env);

  ScopedUtfChars name(env, javaName);

  if (name.c_str() == nullptr) {

    return nullptr;

  }

 

  // 判断类名是否无效

  if (!IsValidBinaryClassName(name.c_str())) {

    soa.Self()->ThrowNewExceptionF("Ljava/lang/ClassNotFoundException;",

                                   "Invalid name: %s", name.c_str());

    return nullptr;

  }

 

  std::string descriptor(DotToDescriptor(name.c_str()));//descriptor指向的是要加载的类的签名

  StackHandleScope<2> hs(soa.Self());

  Handle<mirror::ClassLoader> class_loader(

      hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));//这里通过java的ClassLoader获取nativ层的对应对象,这里获取的是native层的BootClassLoader引用

  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();//获取当前虚拟机的class_linker引用

  Handle<mirror::Class> c(

      hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader)));//调用class_linker的findClass方法,传入虚拟机self,descriptor,class_loader,返回我们要load的class

  if (c == nullptr) {

    ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());

    env->ExceptionClear();

    jthrowable cnfe = reinterpret_cast<jthrowable>(

        env->NewObject(WellKnownClasses::java_lang_ClassNotFoundException,

                       WellKnownClasses::java_lang_ClassNotFoundException_init,

                       javaName,

                       cause.get()));

    if (cnfe != nullptr) {

      // Make sure allocation didn't fail with an OOME.

      env->Throw(cnfe);

    }

    return nullptr;

  }

  if (initialize) {//这个就是传入的是否初始化的判断值

    class_linker->EnsureInitialized(soa.Self(), c, true, true);//对load的Class做初始化操作

  }

  return soa.AddLocalReference<jclass>(c.Get());

}

这个阶段主要根据传入的javaname,java classLoader,获取native层的descriptor 和对应的bootClassLoader。

然后调用class_linker.findClass,加载dex文件并返回Class。

 

2.2 InMemoryDexClassLoader

InMemoryDexClassLoader继承于BaseDexClassLoader,是API26新增的类加载器。dexBuffers数组构造了一个DexPathList,可用于加载内存中的dex

构造函数:

    public InMemoryDexClassLoader(ByteBuffer[] dexBuffers, ClassLoader parent) {

        super(dexBuffers, parent);

}

    public InMemoryDexClassLoader(ByteBuffer dexBuffer, ClassLoader parent) {

        this(new ByteBuffer[] { dexBuffer }, parent);

    }

2.3 PathClassLoader

PathClassLoader 它是用来加载系统类和应用程序类,以及data/app下面的包含dex文件的apk和jar包。

它继承自BaseDexClassLoader,没有复写逻辑,只是进行了封装,它实现了两个构造函数

(1)

public PathClassLoader(String dexPath, ClassLoader parent) {

         super(dexPath, null, null, parent);

}

传入两个参数dexPath 以及parent,它的父加载器parent就是BootClassLoader,而dexPath则是指定的包含dex文件的apk文件或者jar文件路径,可以为多个路径。

这个是本地通过apk获取的自己的类加载器以及父加载器:

ClassLoader myLoader = MainActivity.class.getClassLoader();

int id = 0;

while(myLoader != null){

         Log.d(TAG,id+": ClassLoader = "+myLoader);

         myLoader = myLoader.getParent();

         id++;

}

2027-03-17 19:46:52.304 18560-18560/com.jamous.myapplication D/jamous check handler: 0: ClassLoader = dalvik.system.PathClassLoader[

2027-03-17 19:46:52.305 18560-18560/com.jamous.myapplication D/jamous check handler: 1: ClassLoader = [email protected]

(2)

另一个构造函数增加一个参数librarySearchPath,指定nativeLib库(C/C++库)的路径集合

public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {

         super(dexPath, null, librarySearchPath, parent);

}

所以看PathClassLoader的具体实现,我们还需要关注它的父类BaseDexClassLoader。

我们看下BaseDexClassLoader的构造函数

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

        }

}

我们传入的参数dexPath,librarySearchPath最后都被封装进入了DexPathList这个类中。这个类主要是负责管理dex file的路径以及native lib 库的路径,这个是作为之后加载工作的导入参数。

我们看下这个它保存的数据中 DexPathList和nativeLibraryDirectories,分别是dex文件目录和本地库路径

DexPathList[

  [zip file "/data/app/com.jamous.myapplication-7-XRA5vjetwYR9xv-aCHrA==/base.apk",

   zip file "/data/app/com.jamous.myapplication-7-XRA5vjetwYR9xv-aCHrA==/split_lib_dependencies_apk.apk",

   zip file "/data/app/com.jamous.myapplication-7-XRA5vjetwYR9xv-aCHrA==/split_lib_resources_apk.apk",

   zip file "/data/app/com.jamous.myapplication-7-XRA5vjetwYR9xv-aCHrA==/split_lib_slice_0_apk.apk",

   zip file "/data/app/com.jamous.myapplication-7-XRA5vjetwYR9xv-aCHrA==/split_lib_slice_1_apk.apk",

   zip file "/data/app/com.jamous.myapplication-7-XRA5vjetwYR9xv-aCHrA==/split_lib_slice_2_apk.apk",

   zip file "/data/app/com.jamous.myapplication-7-XRA5vjetwYR9xv-aCHrA==/split_lib_slice_3_apk.apk",

   zip file "/data/app/com.jamous.myapplication-7-XRA5vjetwYR9xv-aCHrA==/split_lib_slice_4_apk.apk",

   zip file "/data/app/com.jamous.myapplication-7-XRA5vjetwYR9xv-aCHrA==/split_lib_slice_5_apk.apk",

   zip file "/data/app/com.jamous.myapplication-7-XRA5vjetwYR9xv-aCHrA==/split_lib_slice_6_apk.apk",

   zip file "/data/app/com.jamous.myapplication-7-XRA5vjetwYR9xv-aCHrA==/split_lib_slice_7_apk.apk",

   zip file "/data/app/com.jamous.myapplication-7-XRA5vjetwYR9xv-aCHrA==/split_lib_slice_8_apk.apk",

   zip file "/data/app/com.jamous.myapplication-7-XRA5vjetwYR9xv-aCHrA==/split_lib_slice_9_apk.apk"

  ],

  nativeLibraryDirectories=[

   /data/app/com.jamous.myapplication-7-XRA5vjetwYR9xv-aCHrA==/lib/arm,

   /system/lib,

   /vendor/lib

  ]

 

我们来看下关键代码:

DexPathList(ClassLoader definingContext, String dexPath,

                   String librarySearchPath, File optimizedDirectory, boolean isTrusted) {

 

//解析dexpath,然后存入dexElements数组

this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,

                                                           suppressedExceptions, definingContext, isTrusted);

//获取app应用的nativeLib 库地址然后存入 nativeLibraryDirectories Filelist

this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);

//根据java.library.path 获取系统lib库的地址路径,存入systemNativeLibraryDirectories

this.systemNativeLibraryDirectories =

                   splitPaths(System.getProperty("java.library.path"), true);

//最后把nativeLibraryDirectories 和 systemNativeLibraryDirectories 统一整合到 allNativeLibraryDirectories

List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);

allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);

//将ArrayList转换为 NativeLibraryElement[] ->nativeLibraryPathElements

this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);

 

我们再继续看下应用启动过程中如何通过pathClassLoader加载自己的dex文件和lib库的

我们以启动Activity为例,

其实Activity的时候,我们会执行到ActivityThread. performLaunchActivity()函数来调用Activity的onCreate方法,那么我们自己实现的Activity是如何第一次被加载呢。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

        ContextImpl appContext = createBaseContextForActivity(r);

Activity activity = null;

        try {

            //此处通过appContext获取ClassLoader,并loadClass(Activityname)来加载

            java.lang.ClassLoader cl = appContext.getClassLoader();

            activity = mInstrumentation.newActivity(

                    cl, component.getClassName(), r.intent);

                                              

    public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className,

            @Nullable Intent intent)

            throws InstantiationException, IllegalAccessException, ClassNotFoundException {

        return (Activity) cl.loadClass(className).newInstance();

    }

再来看appContext的ClassLoader的来源,优先它自己的mClassLoader,如果为null再去找它内部的mPackageInfo的ClassLoader,最后才是getSystemClassLoader获取SystemClassLoader。

/frameworks/base/core/java/android/app/ContextImpl.java

    public ClassLoader getClassLoader() {

        return mClassLoader != null ? mClassLoader : (mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader());

}

......

那么这个时候需要看下createBaseContextForActivity(r)的过程

ContextImpl appContext = ContextImpl.createActivityContext(

                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);

static ContextImpl createActivityContext(ActivityThread mainThread, LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,Configuration overrideConfiguration) {

         ClassLoader classLoader = packageInfo.getClassLoader();

 

Ok,我们知道了appContext的ClassLoader的Classloader用的就是每个传入的ActivityClientRecord. packageInfo. getClassLoader();

packageInfo就是LoadedApk

/frameworks/base/core/java/android/app/LoadedApk.java

    public ClassLoader getClassLoader() {

        synchronized (this) {

            if (mClassLoader == null) {

                createOrUpdateClassLoaderLocked(null /*addedPaths*/);

            }

            return mClassLoader;

        }

}

在createOrUpdateClassLoaderLocked实现ClassLoader的创建,

  1. 如果包名含有android,则使用

mClassLoader = ClassLoader.getSystemClassLoader();

这个最后调用到

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

}

获取PathClassLoader的实例,父加载器为BootClassLoader

  1. 根据dex文件所在的文件路径,通过ApplicationLoaders.getDefault().getClassLoader获取ClassLoader

这个最后调用到ApplicationLoaders.java的getClassLoader方法

使用工厂类创建ClassLoader,主要根据name作为判断

ClassLoader classloader = ClassLoaderFactory.createClassLoader(

                        zip,  librarySearchPath, libraryPermittedPath, parent,

                        targetSdkVersion, isBundled, classLoaderName);

ClassLoaderFactory.java

public static ClassLoader createClassLoader(String dexPath,

            String librarySearchPath, ClassLoader parent, String classloaderName) {

        if (isPathClassLoaderName(classloaderName)) {

            return new PathClassLoader(dexPath, librarySearchPath, parent);

        } else if (isDelegateLastClassLoaderName(classloaderName)) {

            return new DelegateLastClassLoader(dexPath, librarySearchPath, parent);

        }

 

        throw new AssertionError("Invalid classLoaderName: " + classloaderName);

}

 

上文我们知道加载类,在获取ClassLoader后需要调用loadClass来执行具体加载任务,我们看下

Path的该方法,也就是BaseDexClassLoader的该方法。发现没有覆写,使用父类也就是ClassLoader的LoadClass。

实现双亲委托模式,这里我们关注子类的查找,交付给FindClass

    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;

}

BaseDexClassLoader.java

    protected Class<?> findClass(String name) throws ClassNotFoundException {

        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();

        Class c = pathList.findClass(name, suppressedExceptions);

DexPathList.java

根据路径挨个dex文件进行遍历查找Class

    public Class<?> findClass(String name, List<Throwable> suppressed) {

        for (Element element : dexElements) {

            Class<?> clazz = element.findClass(name, definingContext, suppressed);

            if (clazz != null) {

                return clazz;

            }

        }

        public Class<?> findClass(String name, ClassLoader definingContext,

                List<Throwable> suppressed) {

            return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)

                    : null;

        }

DexFile.java

    public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {

        return defineClass(name, loader, mCookie, this, suppressed);

}

然后交付给native方法加载Class result = defineClassNative(name, loader, cookie, dexFile);

Native层也是交付给class_linker进行查找加载。

2.4 DelegateLastClassLoader

DelegateLastClassLoader继承自PathClassLoaderAPI27新增的类加载器, DelegateLastClassLoader实行最后的查找策略。使用DelegateLastClassLoader来加载每个类和资源,使用的是以下顺序:

  1. 判断是否已经加载过该类
  2. 判断此类是否被BootClassLoader加载过

3.  搜索此类是否被当前类加载器是已经加载过

4.  搜索与此类加载器相关联的dexPath文件列表,并委托给父加载器。

    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {

        // First, check whether the class has already been loaded. Return it if that's the

        // case.

        Class<?> cl = findLoadedClass(name);

        if (cl != null) {

            return cl;

        }

 

        // Next, check whether the class in question is present in the boot classpath.

        try {

            return Object.class.getClassLoader().loadClass(name);

        } catch (ClassNotFoundException ignored) {

        }

 

        // Next, check whether the class in question is present in the dexPath that this classloader

        // operates on.

        ClassNotFoundException fromSuper = null;

        try {

            return findClass(name);

        } catch (ClassNotFoundException ex) {

            fromSuper = ex;

        }

 

        // Finally, check whether the class in question is present in the parent classloader.

        try {

            return getParent().loadClass(name);

        } catch (ClassNotFoundException cnfe) {

            // The exception we're catching here is the CNFE thrown by the parent of this

            // classloader. However, we would like to throw a CNFE that provides details about

            // the class path / list of dex files associated with *this* classloader, so we choose

            // to throw the exception thrown from that lookup.

            throw fromSuper;

        }

    }

2.4 DexClassLoader

DexClassLoader: 可以从包含classes.dex的jar或者apk中,加载类的类加载器, 可用于执行动态加载,但必须是app私有可写目录来缓存odex文件. 能够加载系统没有安装的apk或者jar文件, 因此很多插件化方案都是采用DexClassLoader;

同样继承自BaseDexClassLoader,它也是对父类通过构造器进行简单封装,多设置了一个参数

optimizedDirectory ,这个参数代表了dex文件优化后的路径。它会在dexFile实例化阶段有不同的操作。

因为不指定它也就是为null,在创建dexfile的时候只会去app默认路径去找。所以这也是为什么使用它加载外置dex文件的原因。

public DexClassLoader(String dexPath, String optimizedDirectory,

            String librarySearchPath, ClassLoader parent) {

        super(dexPath, null, librarySearchPath, parent);

}