Android Classloader機制

adison發表於2019-01-06

傳統Jvm

java虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Java型別,這就是虛擬機器的類載入機制。

類的生命週期

類從被載入到虛擬機器記憶體中開始,到解除安裝出記憶體為止,它的整個生命週期包括:載入(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和解除安裝(Unloading)7個階段。其中驗證、準備、解析3個部分統稱為連線(Linking),這7個階段的發生順序如圖所示

Android Classloader機制

類載入器

對於任意一個類,都需要由載入它的類載入器和這個類本身一同確立其在Java虛擬機器中的唯一性,每一個類載入器,都擁有一個獨立的類名稱空間。這句話可以表達得更通俗一些:**比較兩個類是否“相等”,只有在這兩個類是由同一個類載入器載入的前提下才有意義,否則,即使這兩個類來源於同一個Class檔案,被同一個虛擬機器載入,只要載入它們的類載入器不同,那這兩個類就必定不相等。 **

雙親委派模型

絕大部分Java程式都會使用到以下3種系統提供的類載入器:

  1. 啟動類載入器(Bootstrap ClassLoader)

這個類將器負責將存放在<JAVA_HOME>\lib目錄中的,或者被-Xbootclasspath引數所指定的路徑中的,並且是虛擬機器識別的(僅按照檔名識別,如rt.jar,名字不符合的類庫即使放在lib目錄中也不會被載入)類庫載入到虛擬機器記憶體中。啟動類載入器無法被Java程式直接引用,使用者在編寫自定義類載入器時,如果需要把載入請求委派給引導類載入器,那直接使用null代替即可。

  1. 擴充套件類載入器(Extension ClassLoader)

這個載入器由sun.misc.Launcher $ExtClassLoader實現,它負責載入<JAVA_HOME>\lib\ext目錄中的,或者被java.ext.dirs系統變數所指定的路徑中的所有類庫,開發者可以直接使用擴充套件類載入器。

  1. 應用程式類載入器(Application ClassLoader)

這個類載入器由sun.misc.Launcher $App-ClassLoader實現。由於這個類載入器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也稱它為系統類載入器。它負責載入使用者類路徑(ClassPath)上所指定的類庫,開發者可以直接使用這個類載入器,如果應用程式中沒有自定義過自己的類載入器,一般情況下這個就是程式中預設的類載入器。

java應用程式一般都是由這3種類載入器互相配合進行載入的,如果有必要,還可以加入自己定義的類載入器。這些類載入器之間的關係一般如圖所示。

Android Classloader機制

上圖中展示的類載入器之間的這種層次關係,稱為類載入器的雙親委派模型(Parents Delegation Model)。雙親委派模型要求除了頂層的啟動類載入器外,其餘的類載入器都應當有自己的父類載入器。這裡類載入器之間的父子關係一般不會以繼承(Inheritance)的關係來實現,而是都使用組合(Composition)關係來複用父載入器的程式碼。

雙親委派模型的工作過程是:如果一個類載入器收到了類載入的請求,它首先不會自己去嘗試載入這個類,而是把這個請求委派給父類載入器去完成,每一個層次的類載入器都是如此,因此所有的載入請求最終都應該傳送到頂層的啟動類載入器中,只有當父載入器反饋自己無法完成這個載入請求(它的搜尋範圍中沒有找到所需的類)時,子載入器才會嘗試自己去載入。

Android的ClassLoader機制

本質上,Android和傳統的JVM是一樣的,也需要通過ClassLoader 將目標類載入到記憶體,類載入器之間也符合雙親委派模型,類也有對應的生命週期。但基於移動裝置的特點,如記憶體以及電量等諸多方面跟一般的 PC 裝置都有本質的區別,Google開發了更符合移動裝置的用於執行 Java 程式碼的虛擬機器,也就是Dalvik和 ART,Android從5.0開始就採用AR虛擬機器替代Dalvik。傳統Jvm主要是通過讀取class位元組碼來載入, 而ART則是從dex位元組碼來讀取. 這是一種更為優化的方案, 可以將多個.class檔案合併成一個classes.dex檔案。

Classloader關係圖

Android Classloader機制

BaseDexClassLoader

public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;
    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, librarySearchPath, null);
    }
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        ...
        ...
        Class c = pathList.findClass(name, suppressedExceptions);
        ...
        ...
        return c;
    }
    @Override
    protected URL findResource(String name) {
        return pathList.findResource(name);
    }
    @Override
    public String findLibrary(String name) {
        return pathList.findLibrary(name);
    }
}
複製程式碼

可以看到在建構函式裡初始化了DexPathList物件,而在BaseDexClassLoader中的操作findClassfindResource執行的都是這個DexPathList物件的操作,關於DexPathList,在此暫不展開。

從DexPathList的構造過程可以看到,無論optimizedDirectory是何值,傳遞的都是空,所以optimizedDirectory引數是無效的(從Android8.0開始)

PathClassLoader

public class PathClassLoader extends BaseDexClassLoader {

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

    public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
    }
}
複製程式碼

PathClassLoader比較簡單, 繼承於BaseDexClassLoader. 預設 optimizedDirectory=null.

DexClassLoader

public class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
    }
}
複製程式碼

DexClassLoader也比較簡單, 只是簡單封裝,和PathClassLoader唯一區別就是多了optimizedDirectory引數,但從上面BaseDexClassLoader分析可以知道,從8.0開始optimizedDirectory已經棄用。從理論上來說,PathClassLoader應該可以完全替代DexClassLoader。但網上有這樣的結論:

DexClassLoader:能夠載入未安裝的apk

PathClassLoader:只能載入系統中已經安裝過的apk

那麼在8.0以上這個結論還成立嗎,事實上PathClassLoader也可以載入未安裝的apk,驗證過程比較簡單,不在此累贅,有興趣可以自己試試。

參考

www.jianshu.com/p/78f9a3f55…

相關文章