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