DexClassLoader和PathClassLoader的區別

    在使用Java虛擬機時,咱們常常自定義繼承自ClassLoader的類加載器。而後經過defineClass方法來從一個二進制流中加載Class。而在Android中咱們沒法這麼使用,Android中ClassLoader的defineClass方法具體是調用VMClassLoader的defineClass本地靜態方法。而這個本地方法什麼都沒作,只是拋出了一個「UnsupportedOperationException」異常。
    既然在Dalvik虛擬機裏,ClassLoader很差用,那麼Android官方爲了解決這個問題,幫咱們從ClassLoader中派生出了兩個類:DexClassLoaderPathClassLoader。咋一看二者很像,那麼究竟兩者在使用上面有何不一樣,這裏我和你們一塊兒探討一下。java

首先來看一下兩者的構造方法緩存

DexClassLoader

public DexClassLoader (String dexPath, String dexOutputDir, String libPath, ClassLoader parent)

參數詳解:markdown

dexPath:dex文件路徑列表,多個路徑使用」:」分隔
dexOutputDir:通過優化的dex文件(odex)文件輸出目錄
libPath:動態庫路徑(將被添加到app動態庫搜索路徑列表中)
parent:這是一個ClassLoader,這個參數的主要做用是保留java中ClassLoader的委託機制(優先父類加載器加載classes,由上而下的加載機制,防止重複加載類字節碼)網絡

DexClassLoader是一個能夠從包含classes.dex實體的.jar或.apk文件中加載classes的類加載器。能夠用於實現dex的動態加載、代碼熱更新等等。這個類加載器必需要一個app的私有、可寫目錄來緩存通過優化的classes(odex文件),使用Context.getDir(String, int)方法能夠建立一個這樣的目錄,例如: app

File dexOutputDir = context.getDir(「dex」, 0);

PathClassLoader

PathClassLoader提供兩個經常使用構造方法優化

public PathClassLoader (String path, ClassLoader parent)
public PathClassLoader (String path, String libPath, ClassLoader parent)

參數詳解:ui

path:文件或者目錄的列表
libPath:包含lib庫的目錄列表
parent:父類加載器this

PathClassLoader提供一個簡單的ClassLoader實現,能夠操做在本地文件系統的文件列表或目錄中的classes,但不能夠從網絡中加載classes。spa

爲了便於理解,咱們查看一下兩者的源碼:.net

這裏寫圖片描述

// DexClassLoader.java
public class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}

// 版權全部,猴子搬來的救兵http://blog.csdn.net/mynameishuangshuai

// PathClassLoader.java
public class PathClassLoader extends BaseDexClassLoader {
    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }

    public PathClassLoader(String dexPath, String libraryPath,
            ClassLoader parent) {
        super(dexPath, null, libraryPath, parent);
    }
}

很明顯二者都繼承於BaseDexClassLoader類,並作了一下封裝,具體的實現仍是在父類裏。不難看出,主要的區別在於PathClassLoader的optimizedDirectory參數只能是null,那麼optimizedDirectory是作什麼用的呢?咱們進BaseDexClassLoader去看看這個參數。

public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(parent);
        this.originalPath = dexPath;
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    }

代碼中與optimizedDirectory有關的地方是new 一個DexPathList實例。

public DexPathList(ClassLoader definingContext, String dexPath,
            String libraryPath, File optimizedDirectory) {
        ……
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory);
    }

    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 = loadDexFile(file, optimizedDirectory);
            } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
                    || name.endsWith(ZIP_SUFFIX)) {
                zip = new ZipFile(file);
            }
            ……
            if ((zip != null) || (dex != null)) {
                elements.add(new Element(file, zip, dex));
            }
        }
        return elements.toArray(new Element[elements.size()]);
    }

    private static DexFile loadDexFile(File file, File optimizedDirectory)
            throws IOException {
        if (optimizedDirectory == null) {
            return new DexFile(file);
        } else {
            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
            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();
    }

optimizedDirectory是用來緩存咱們須要加載的dex文件的,並建立一個DexFile對象,若是它爲null,那麼會直接使用dex文件原有的路徑來建立DexFile
對象。

optimizedDirectory必須是一個內部存儲路徑,不管哪一種動態加載,加載的可執行文件必定要存放在內部存儲。DexClassLoader能夠指定本身的optimizedDirectory,因此它能夠加載外部的dex,由於這個dex會被複制到內部路徑的optimizedDirectory;而PathClassLoader沒有optimizedDirectory,因此它只能加載內部的dex,這些大都是存在系統中已經安裝過的apk裏面的。

經過以上的分析,咱們能夠得出兩者功能上的區別

DexClassLoader:可以加載未安裝的jar/apk/dex
PathClassLoader:只能加載系統中已經安裝過的apk

參考連接:
http://www.jianshu.com/p/669fc4858194

相關文章
相關標籤/搜索