android PathClassLoader DexClassLoader BaseDexClassLoader

来源:互联网 发布:hpm136扫描软件 编辑:程序博客网 时间:2024/05/13 17:52

android PathClassLoader DexClassLoader BaseDexClassLoader

Android类加载器:
BootClassLoader,URLClassLoader,PathClassLoader,DexClassLoader,BaseDexClassLoader等最终都继承自java.lang.ClassLoader,

//1、ClassLoader中loadClass()函数
Android中的ClassLoader与Java有些不同,Android中ClassLoader加载的是dex文件,而Java中加载的是jar文件.相同的是两者都采用了双亲委派模型.
其他的子类都继承了此方法且没有进行复写.

protected Class<?> loadClass(String className, boolean resolve)     throws ClassNotFoundException {    Class<?> clazz = findLoadedClass(className);    // 检查Class是否已加载过    if (clazz == null) {        ClassNotFoundException suppressed = null;        try {            clazz = parent.loadClass(className, false);            //使用parent ClassLoader去加载Class        } catch (ClassNotFoundException e) {            suppressed = e;        }        if (clazz == null) {            try {                clazz = findClass(className);                // 加载不成功,调用findClass函数来获取class对象.            } catch (ClassNotFoundException e) {                e.addSuppressed(suppressed);                throw e;            }        }    }    return clazz;}

2、PathClassLoader和DexClassLoader都继承自BaseDexClassLoader,其主要逻辑都是在BaseDexClassLoader完成
public class BaseDexClassLoader extends ClassLoader{

/**
* Constructs an instance.
*
* @param dexPath:the list of jar/apk files containing classes and resources, delimited by {@code File.pathSeparator}, which defaults to {@code “:”} on Android
* @param optimizedDirectory:directory where optimized dex files should be written; may be {@code null}
* @param libraryPath:the list of directories containing native libraries, delimited by {@code File.pathSeparator}; may be{@code null}
* @param parent:the parent class loader
*/
/*
dexPath:
指目标类所在的APK或jar文件的路径, 类装载器将从该路径中寻找指定的目标类, 该类必须是APK或jar的全路径.
如果要包含多个路径,路径之间必须使用特定的分割符分隔,特定的分割符可以使用System.getProperty(“path.separtor”)获得。
上面”支持加载APK、DEX和JAR,也可以从SD卡进行加载”指的就是这个路径,
最终做的是将dexPath路径上的文件ODEX优化到内部位置optimizedDirectory,然后再进行加载的。
optimizedDirectory: (必须是一个内部存储路径,是用来缓存我们需要加载的dex文件的,并创建一个DexFile对象,如果它为null,直接使用dex文件原有的路径来创建DexFile对象)
由于dex文件被包含在APK或者Jar文件中,因此在装载目标类之前需要先从APK或Jar文件中解压出dex文件,该参数就是制定解压出的dex文件存放的路径。
这也是对apk中dex根据平台进行ODEX优化的过程。其实APK是一个程序压缩包,里面包含dex文件,ODEX优化就是把包里面的执行程序提取出来,就变成ODEX文件,
只有第一次会解压执行程序到 /data/dalvik-cache(针对PathClassLoader)或者optimizedDirectory(针对DexClassLoader)目录,之后是直接读取目录下的的dex文件。
libPath:
指目标类中所使用的C/C++库存放的路径
classload:
是指该装载器的父装载器,一般为当前执行类的装载器,例如在Android中以context.getClassLoader()作为父装载器
*/

public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) {        super(parent);        this.originalPath = dexPath;        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);          //BaseDexClassLoder的构造函数使用入参的三个路径构造了一个DexPathList对象.// DexPathList -> Element(makeDexElements()) -> DexFile(loadDexFile()->loadDex()->DexFile()->openDexFile())    }    @Override    protected Class<?> findClass(String name) throws ClassNotFoundException {         Class clazz = pathList.findClass(name);         //使用DexPathListfindClass()函数,返回找到的.        if (clazz == null) {            throw new ClassNotFoundException(name);        }        return clazz;    }    //more code

//DexClassLoader还需要指定一个生成优化后的apk的路径optimizedDirectory。而PathClassLoader则不需要,因为在安装阶段已经生成了/data/dalvik-cache/xxx@classes.dex
//可以从包含classes.dex实体的.jar或.apk文件中加载classes的类加载器。可以用于实现dex的动态加载、代码热更新等

public class DexClassLoader extends BaseDexClassLoader { //可以加载jar/apk/dex,可以从SD卡中加载未安装的apk

public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) {         super(dexPath, new File(optimizedDirectory), libraryPath, parent);         //直接调用BaseDexClassLoader的构造函数    }}

//PathClassLoader的super调用中,optimizedDirectory一直为null.
(也就是没设置优化后的存放路径optimizedDirectory,实际上optimizedDirectory为null时的默认路径就是/data/dalvik-cache目录,PathClassLoader是用来加载Android系统类和应用的类,不建议开发者直接使用)

public class PathClassLoader extends BaseDexClassLoader { //只能加载系统中已经安装过的apk    public PathClassLoader(String dexPath, ClassLoader parent) {        super(dexPath, null, null, parent);         //直接调用BaseDexClassLoader的构造函数    }    public PathClassLoader(String dexPath, String libraryPath, ClassLoader parent) {        super(dexPath, null, libraryPath, parent);    }}

3、

final class DexPathList {    /** list of dex/resource (class path) elements */    private final Element[] dexElements;    public DexPathList(ClassLoader definingContext, String dexPath,            String libraryPath, File optimizedDirectory) {        // some error checking        this.definingContext = definingContext;        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory); //传入的dexPath、optimizedDirectory路径转化为Element数组        this.nativeLibraryDirectories = splitLibraryPath(libraryPath);    }    public Class findClass(String name) {        for (Element element : dexElements) { //遍历dexElements中的DexFile来加载Class            DexFile dex = element.dexFile;            if (dex != null) {                Class clazz = dex.loadClassBinaryName(name, definingContext);                if (clazz != null) {                    return clazz;                }            }        }        return null;    }    static class Element {        public final File file;        public final ZipFile zipFile;        public final DexFile dexFile;    }}

//Element数组的生成过程

/** * Makes an array of dex/resource path elements, one per element of * the given array. */private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory) {    ArrayList<Element> elements = new ArrayList<Element>();    for (File file : files) {        ZipFile zip = null;        DexFile dex = null;        String name = file.getName();        if (name.endsWith(DEX_SUFFIX)) { // 文件后缀为.dex            try {                dex = loadDexFile(file, optimizedDirectory); //1、 loadDexFile()来生成dex对象            } catch (IOException ex) {                System.logE("Unable to load dex file: " + file, ex);            }        } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX) || name.endsWith(ZIP_SUFFIX)) {// 文件后缀为apk,jar,zip            try {                zip = new ZipFile(file); //构造zip对象            } catch (IOException ex) {                System.logE("Unable to open zip file: " + file, ex);            }            try {                dex = loadDexFile(file, optimizedDirectory); //2、loadDexFile()来生成dex对象            } catch (IOException ignored) {                 // 如果压缩文件中没有dex文件,抛出这个异常,可以直接无视            }        } else {            System.logW("Unknown file type for: " + file);        }        if ((zip != null) || (dex != null)) { //如果同时有zip和dex文件,就构造对应的Element            elements.add(new Element(file, zip, dex));         }    }    return elements.toArray(new Element[elements.size()]);}typedef struct DexFile {      /* directly-mapped "opt" header */      const DexOptHeader* pOptHeader;      /* pointers to directly-mapped structs and arrays in base DEX */      const DexHeader*    pHeader;      const DexStringId*  pStringIds;      const DexTypeId*    pTypeIds;      const DexFieldId*   pFieldIds;      const DexMethodId*  pMethodIds;      const DexProtoId*   pProtoIds;      const DexClassDef*  pClassDefs;      const DexLink*      pLinkData;      /*      * These are mapped out of the "auxillary" section, and may not be      * included in the file.      */      const DexClassLookup* pClassLookup;      const void*         pRegisterMapPool;       // RegisterMapClassPool      /* points to start of DEX file data */      const u1*           baseAddr;      /* track memory overhead for auxillary structures */      int                 overhead;      /* additional app-specific data structures associated with the DEX */      //void*               auxData;  } DexFile; 

//loadDexFile()调用loadDex()创建 DexFile

private static DexFile loadDexFile(File file, File optimizedDirectory)        throws IOException {    if (optimizedDirectory == null) {        return new DexFile(file);    } else {        String optimizedPath = optimizedPathFor(file, optimizedDirectory); //根据optimizedDirectory是否为null调用不同的方法来构造DexFile        return DexFile.loadDex(file.getPath(), optimizedPath, 0);     }}/**   * Converts a dex/jar file path and an output directory to an   * output file path for an associated optimized dex file.   */private static String optimizedPathFor(File path, File optimizedDirectory) {   String fileName = path.getName();   if (!fileName.endsWith(DEX_SUFFIX)) {      int lastDot = fileName.lastIndexOf(".");      if (lastDot < 0) {         fileName += DEX_SUFFIX;      } else {         StringBuilder sb = new StringBuilder(lastDot + 4);         sb.append(fileName, 0, lastDot);         sb.append(DEX_SUFFIX);         fileName = sb.toString();      }   }   File result = new File(optimizedDirectory, fileName);   return result.getPath();}

//loadDex()实例化DexFile类

static public DexFile loadDex(String sourcePathName, String outputPathName,  int flags) throws IOException {       /*      * TODO: we may want to cache previously-opened DexFile objects.      * The cache would be synchronized with close().  This would help      * us avoid mapping the same DEX more than once when an app decided to open it multiple times.  In practice this may not be a real issue.      */      return new DexFile(sourcePathName, outputPathName, flags);  } 

//DexFile类调用openDexFile()

private DexFile(String sourceName, String outputName, int flags) throws IOException {      String wantDex = System.getProperty("android.vm.dexfile", "false");      if (!wantDex.equals("true"))          throw new UnsupportedOperationException("No dex in this VM");      mCookie = openDexFile(sourceName, outputName, flags); // 调用openDexFile() (native函数)       mFileName = sourceName;      //System.out.println("DEX FILE cookie is " + mCookie);  }

// java code

native private static int openDexFile(String sourceName, String outputName, int flags) throws IOException; // outputName就是optimizedDirectory,PathClassLoader和DexClassLoader的区别是optimizedDirectory是否为null,对应openDexFile()中outputName是否为null

// native code

static void Dalvik_dalvik_system_DexFile_openDexFile(const u4* args, JValue* pResult)  //加载dex文件的过程在dvmRawDexFileOpen()和dvmJarFileOpen()方法中完成{      ......      if (dvmRawDexFileOpen(sourceName, outputName, &pRawDexFile, false) == 0) {          LOGV("Opening DEX file '%s' (DEX)\n", sourceName);          pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));          pDexOrJar->isDex = true;          pDexOrJar->pRawDexFile = pRawDexFile;      } else if (dvmJarFileOpen(sourceName, outputName, &pJarFile, false) == 0) {          LOGV("Opening DEX file '%s' (Jar)\n", sourceName);          pDexOrJar = (DexOrJar*) malloc(sizeof(DexOrJar));          pDexOrJar->isDex = false;          pDexOrJar->pJarFile = pJarFile;      } else {          LOGV("Unable to open DEX file '%s'\n", sourceName);          dvmThrowException("Ljava/io/IOException;", "unable to open DEX file");      }      ......      RETURN_PTR(pDexOrJar);  }  
原创粉丝点击