本篇内容主要讲解“Java中的ClassLoader怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java中的ClassLoader怎么使用”吧!

一、前言

一个完整的Java应用程序,当程序在运行时,即会调用该程序的一个入口函数来调用系统的相关功能,而这些功能都被封装在不同的class文件当中,所以经常要从这个class文件中要调用另外一个class文件中的方法,如果另外一个文件不存在的,则会引发系统异常。而程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存之后,才能被其它class所引用。所以ClassLoader就是用来动态加载class文件到内存当中用的。

Android平台上虚拟机运行的是Dex字节码,一种对class文件优化的产物,传统Class文件是一个Java源码文件会生成一个.class文件,而Android是把所有Class文件进行合并,优化,然后生成一个最终的class.dex,目的是把不同class文件重复的东西只需保留一份,如果我们的Android应用不进行分dex处理,最后一个应用的apk只会有一个dex文件。

二、java 中的 ClassLoader

BootstrapClassLoader
负责加载 JVM 运行时的核心类,比如 JAVA_HOME/lib/rt.jar 等等

ExtensionClassLoader
负责加载 JVM 的扩展类,比如 JAVA_HOME/lib/ext 下面的 jar 包

AppClassLoader
负责加载 classpath 里的 jar 包和目录

三、Android 中的 ClassLoader

BootClassLoader

负责 Android系统启动时会使用BootClassLoader来预加载常用类,与Java中的Bootstrap ClassLoader不同的是,它并不是由C/C++代码实现,而是由Java实现的。BootClassLoader是ClassLoader的一个内部类。
PathClassLoader

负责加载已经安装的Apk,也就是/data/app/package 下的apk文件,也可以加载/vendor/lib, /system/lib下的nativeLibrary

DexClassLoader

负责加载可以加载一个未安装的apk文件。

四、双亲委派机制

每一个 ClassLoader 中都有一个 parent 对象,代表的是父类加载器,在加载一个类的时候,会先使用父类加载器去加载,如果在父类加载器中没有找到,自己再进行加载,如果 parent 为空,那么就用系统类加载器来加载。通过这样的机制可以保证系统类都是由系统类加载器加载的。 下面是 ClassLoader 的 loadClass 方法的具体实现。

protectedClass<?>loadClass(Stringname,booleanresolve)throwsClassNotFoundException{//First,checkiftheclasshasalreadybeenloadedClass<?>c=findLoadedClass(name);if(c==null){try{if(parent!=null){//先从父类加载器中进行加载c=parent.loadClass(name,false);}else{c=findBootstrapClassOrNull(name);}}catch(ClassNotFoundExceptione){//ClassNotFoundExceptionthrownifclassnotfound//fromthenon-nullparentclassloader}if(c==null){//没有找到,再自己加载c=findClass(name);}}returnc;}五、源码分析

1.现在我们看下BaseDexClassLoader继承自ClassLoader

publicclassBaseDexClassLoaderextendsClassLoader{...//存放需要加载的dexListprivatefinalDexPathListpathList;/****@paramdexPath需要加载的dex文件所在的路径*@paramoptimizedDirectoryAndroid系统将dex文件进行优化后所生成的ODEX文件的存放路径,该路径必须是一个内部存储路径。*@paramlibrarySearchPath目标类所使用的c、c++库存放的路径*@paramparent该加载器的父加载器,一般为当前执行类的加载器*/publicBaseDexClassLoader(StringdexPath,FileoptimizedDirectory,StringlibrarySearchPath,ClassLoaderparent){this(dexPath,optimizedDirectory,librarySearchPath,parent,false);}/****@paramdexPath*@paramoptimizedDirectory*@paramlibrarySearchPath*@paramparent*@paramisTrusted是否已信任,关系到是否可调用隐藏API*/publicBaseDexClassLoader(StringdexPath,FileoptimizedDirectory,StringlibrarySearchPath,ClassLoaderparent,booleanisTrusted){super(parent);this.pathList=newDexPathList(this,dexPath,librarySearchPath,null,isTrusted);...}/****@paramdexFiles字节缓存数组的dex文件*@paramparent该加载器的父加载器*/publicBaseDexClassLoader(ByteBuffer[]dexFiles,ClassLoaderparent){//TODOWeshouldsupportgivingthisalibrarysearchpathmaybe.super(parent);this.pathList=newDexPathList(this,dexFiles);}/***通过完整的类名寻找对应的类*@paramname传入一个完整的类名*@return*@throwsClassNotFoundException*/@OverrideprotectedClass<?>findClass(Stringname)throwsClassNotFoundException{List<Throwable>suppressedExceptions=newArrayList<Throwable>();//1在pathList中寻找name对应的类Classc=pathList.findClass(name,suppressedExceptions);//如果未找到此类,则抛出异常if(c==null){ClassNotFoundExceptioncnfe=newClassNotFoundException("Didn'tfindclass""+name+""onpath:"+pathList);for(Throwablet:suppressedExceptions){cnfe.addSuppressed(t);}throwcnfe;}returnc;}}

PathClassLoader 和DexClassLoader: 继承自BaseDexClassLoader

publicclassPathClassLoaderextendsBaseDexClassLoader{/****@paramdexPathdex文件路径集合*@paramparent父加载器*/publicPathClassLoader(StringdexPath,ClassLoaderparent){//调用父类BaseDexClassLoader四参构造方法super(dexPath,null,null,parent);}/****@paramdexPathdex文件路径集合*@paramlibrarySearchPath包含C/C++库的路径集合,多个路径用文件分隔符分隔分割,可以为null*@paramparent父加载器*/publicPathClassLoader(StringdexPath,StringlibrarySearchPath,ClassLoaderparent){//调用父类BaseDexClassLoader四参构造方法super(dexPath,null,librarySearchPath,parent);}}

publicclassDexClassLoaderextendsBaseDexClassLoader{/****@paramdexPathdex文件路径集合,多个路径用文件分隔符分隔,默认文件分隔符为":"*@paramoptimizedDirectory解压的dex文件存储路径,这个路径必须是一个内部存储路径,一般情况下使用当前应用程序的私有路径*@paramlibrarySearchPath包含C/C++库的路径集合,多个路径用文件分隔符分隔分割,可以为null*@paramparent父加载器*/publicDexClassLoader(StringdexPath,StringoptimizedDirectory,StringlibrarySearchPath,ClassLoaderparent){//调用父类BaseDexClassLoader四参构造方法,在API26以上,librarySearchPath参数已弃用,使用此方法super(dexPath,null,librarySearchPath,parent);//API26及以下使用此方法//super(dexPath,newFile(optimizedDirectory),librarySearchPath,parent);}}

我们看1 处pathList 的 findClass是如何查找的

/*package*/finalclassDexPathList{privatestaticfinalStringDEX_SUFFIX=".dex";privatestaticfinalStringzipSeparator="!/";privatefinalClassLoaderdefiningContext;//dex/resource存放dex的数组privateElement[]dexElements;//存放本地库文件的列表privatefinalList<File>nativeLibraryDirectories;//存放系统本地库文件的列表privatefinalList<File>systemNativeLibraryDirectories;//存放创建dexElement列表时引发异常的列表privateIOException[]dexElementsSuppressedExceptions;DexPathList(ClassLoaderdefiningContext,StringdexPath,StringlibrarySearchPath,FileoptimizedDirectory,booleanisTrusted){...this.definingContext=definingContext;//BaseDexClassLoader构造器中会传入其本身ArrayList<IOException>suppressedExceptions=newArrayList<IOException>();//通过dexPath路径使用分隔符将其转换成dexElements列表this.dexElements=makeDexElements(splitDexPath(dexPath),optimizedDirectory,suppressedExceptions,definingContext,isTrusted);...}//为本地库搜索路径生成一个directory/zippath元素数组privatestaticElement[]makeDexElements(List<File>files,FileoptimizedDirectory,List<IOException>suppressedExceptions,ClassLoaderloader,booleanisTrusted){Element[]elements=newElement[files.size()];intelementsPos=0;for(Filefile:files){if(file.isDirectory()){//如果是文件夹,则直接添加至elementselements[elementsPos++]=newElement(file);}elseif(file.isFile()){//如果是文件Stringname=file.getName();DexFiledex=null;//是否为.dex文件if(name.endsWith(DEX_SUFFIX)){//Rawdexfile(notinsideazip/jar).try{//如果是dex文件,则加载这个文件dex=loadDexFile(file,optimizedDirectory,loader,elements);if(dex!=null){//将dex文件保存,注意第二个参数传的底nullelements[elementsPos++]=newElement(dex,null);}}catch(IOExceptionsuppressed){System.logE("Unabletoloaddexfile:"+file,suppressed);suppressedExceptions.add(suppressed);}}else{//如果不是目录且不是.dex结尾的,那么他可能是jar。加载这个文件dex=loadDexFile(file,optimizedDirectory,loader,elements);if(dex==null){elements[elementsPos++]=newElement(file);}else{//保存dex文件和当前的file,这种情况下可能是jar,具体可以看这个构造方法elements[elementsPos++]=newElement(dex,file);}if(dex!=null&&isTrusted){//如果dex对象不为空且是允许信任状态dex.setTrusted();//将此dex对象设置为已信任,它可以访问平台的隐藏api}}else{System.logW("ClassLoaderreferencedunknownpath:"+file);}}if(elementsPos!=elements.length){elements=Arrays.copyOf(elements,elementsPos);}returnelements;}publicClass<?>findClass(Stringname,List<Throwable>suppressed){//遍历dex列表for(Elementelement:dexElements){//2Class<?>clazz=element.findClass(name,definingContext,suppressed);//如果找到我们需要的name类,直接返回当前clazzif(clazz!=null){returnclazz;}}if(dexElementsSuppressedExceptions!=null){suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));}returnnull;}}

看注释2处 此时会通过makeDexElements方法生成一个Element数组,紧接着当前类中的findClass方法又会调用DexPathList中的Element类的findClass方法。

staticclassElement{privatefinalFilepath;privatefinalDexFiledexFile;privateClassPathURLStreamHandlerurlHandler;privatebooleaninitialized;......publicClass<?>findClass(Stringname,ClassLoaderdefiningContext,List<Throwable>suppressed){//3通过loadClassBinaryName方法寻找name类,找到即返回它,否则返回nullreturndexFile!=null?dexFile.loadClassBinaryName(name,definingContext,suppressed):null;}}

注释3 处通过loadClassBinaryName 最后调用native层defineClassNative的方法分析到这里可以看出,最终进行Class字节码的加载操作,是通过底层的native方法来完成的。

到此,相信大家对“Java中的ClassLoader怎么使用”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!