android PathClassLoader DexClassLoader BaseDexClassLoader

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);  
}  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值