標籤:
郭嘉
郵箱:[email protected]
部落格:http://blog.csdn.net/allenwells
github:https://github.com/AllenWell
在介紹Android的類載入機制之前,我們需要先瞭解一下Java的類載入機制。
【Java 安全技術探索之路系列:J2SE安全架構】之五:類載入器
一 Dalvik虛擬機器與Java虛擬機器
Dalvik虛擬機器如同其他Java虛擬機器一樣,在運行程式時首先需要將對應的類載入到記憶體中。而在Java標準的虛擬機器中,類載入可以從class檔案中讀取,也可以是其他形式的二進位流。因此,我們常常利用這一點,在程式運行時手動載入Class,從而達到代碼動態載入執行的目的。
然而Dalvik虛擬機器畢竟不算是標準的Java虛擬機器,因此在類載入機制上,它們有相同的地方,也有不同之處。我們必須區別對待。例如,在使用標準Java虛擬機器時,我們經常自訂繼承自ClassLoader的類載入器。然後通過defineClass方法來從一個二進位流中載入Class。然而,這在Android裡是行不通的,
二 Android類載入器
Android的類載入器主要有兩個PathClassLoader和DexClassLoader,其中PathClassLoader是預設的類載入器,下面我們就來說說兩者的區別與聯絡。
- PathClassLoader:支援載入DEX或者已經安裝的APK(因為存在緩衝的DEX)。
- DexClassLoader:支援載入APK、DEX和JAR。
DexClassLoader和PathClassLoader都屬於符合雙親委派模型的類載入器(因為它們沒有重載loadClass方法)。也就是說,它們在載入一個類之前,回去檢查自己以及自己以上的類載入器是否已經載入了這個類。如果已經載入過了,就會直接將之返回,而不會重複載入。
無論是PathClassLoader還是DexClassLoader都是繼承於BaseClassLoader,那麼我們就先來看一下BaseCLassLoader的實現。
2.1 BaseClassLoader
【android-21】BaseDexClassLoader的源碼如下所示:
package dalvik.system;import java.io.File;import java.net.URL;import java.util.Enumeration;public class BaseDexClassLoader extends ClassLoader{ public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) { throw new RuntimeException("Stub!"); } protected Class<?> findClass(String name) throws ClassNotFoundException { throw new RuntimeException("Stub!"); } protected URL findResource(String name) { throw new RuntimeException("Stub!"); } protected Enumeration<URL> findResources(String name) { throw new RuntimeException("Stub!"); } public String findLibrary(String name) { throw new RuntimeException("Stub!"); } protected synchronized Package getPackage(String name) { throw new RuntimeException("Stub!"); } public String toString() { throw new RuntimeException("Stub!"); }}
2.2 PathClassLoader
【android-21】PathClassLoader的源碼如下所示:
package dalvik.system;import java.io.File;public class PathClassLoader extends BaseDexClassLoader{ public PathClassLoader(String dexPath, ClassLoader parent) { super((String)null, (File)null, (String)null, (ClassLoader)null);throw new RuntimeException("Stub!"); } public PathClassLoader(String dexPath, String libraryPath, ClassLoader parent) { super((String)null, (File)null, (String)null, (ClassLoader)null);throw new RuntimeException("Stub!"); }}
從源碼可以看出,PathClassLoader有兩個建構函式,函數中的參數含義如下所示:
- String dexPath:載入APK、DEX和JAR的路徑。
- String libraryPath:載入DEX的時候需要用到的lib庫,libraryPath一般包括/vendor/lib和/system/lib。
- ClassLoader parent:DEXClassLoader指定的父類載入器
PathClassLoader
2.3 DexClassLoader
【android-21】DexClassLoader的源碼如下所示:
package dalvik.system;import java.io.File;public class DexClassLoader extends BaseDexClassLoader{ public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) { super((String)null, (File)null, (String)null, (ClassLoader)null);throw new RuntimeException("Stub!"); }}
從源碼可以看出,DexClassLoader只有一個建構函式,該函數中的參數含義如下所示:
- String dexPath:載入APK、DEX和JAR的路徑。
- String optimizedDirectory:是DEX的輸出路徑。
- String libraryPath:載入DEX的時候需要用到的lib庫,libraryPath一般包括/vendor/lib和/system/lib。
- ClassLoader parent:DEXClassLoader指定的父類載入器
大家可以發現DexClassLoader的建構函式比PathClassLoader多了一個String optimizedDirectory參數,這是因為PathClassLoader是載入/data/app中的APK,而這部分的APK都會解壓釋放dex到指定的目錄。
2.4 DexFile
DexClassLoader和PathClassLoader其實都是通過DexFile這個類來實作類別載入的。這裡需要順便提一下的是,Dalvik虛擬機器識別的是dex檔案,而不是class檔案。因此,我們供類載入的檔案也只能是dex檔案,或者包含有dex檔案的.apk或.jar檔案。
也許有人想到,既然DexFile可以直接載入類,那麼我們為什麼還要使用ClassLoader的子類呢?DexFile在載入類時,具體是調用成員方法loadClass或者loadClassBinaryName。其中loadClassBinaryName需要將包含包名的類名中的”.”轉換為”/”。我們看一下loadClass代碼就清楚了。
【android-21】DexFile源碼如下所示:
package dalvik.system;import java.io.File;import java.io.FileNotFoundException;import java.io.IOException;import java.util.Enumeration;public final class DexFile{ public DexFile(File file) throws IOException { throw new RuntimeException("Stub!"); } public DexFile(String fileName) throws IOException { throw new RuntimeException("Stub!"); } public static DexFile loadDex(String sourcePathName, String outputPathName, int flags) throws IOException { throw new RuntimeException("Stub!"); } public String getName() { throw new RuntimeException("Stub!"); } public String toString() { throw new RuntimeException("Stub!"); } public void close() throws IOException { throw new RuntimeException("Stub!"); } public Class loadClass(String name, ClassLoader loader) { throw new RuntimeException("Stub!"); } public Enumeration<String> entries() { throw new RuntimeException("Stub!"); } protected void finalize() throws Throwable { throw new RuntimeException("Stub!"); } public static native boolean isDexOptNeeded(String paramString) throws FileNotFoundException, IOException;}
三 Android類載入機制3.1類載入器結構3.1.1 系統類別載入器
舉例
Context.class.getClassLoader();
上述代碼得到的結果表明系統類別的載入器是BootClassLoader。
ClassLoader.getSystemClassLoader().getParent();
上述代碼錶明系統載入器的父類載入器還是
3.2.2 應用程式載入器
舉例
getClassLoader();
上述代碼得到的結果表明應用程式的載入器是PathClassLoader
getClassLoader().getParent();
上述代碼得到的結果表明應用程式的家在啟動餓父類載入器是BootClassLoader。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
Android類載入器