您好,登錄后才能下訂單哦!
本篇內容介紹了“Android中的類文件和類加載器實例分析”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
首先花點時間回顧一下Java中的三種類加載器:
BootStrap ClassLoader 啟動類加載器,它是實現自C/C++的類加載器,用于加載JDK的核心類庫,例如java.lang
、java.util
等系統類。JVM的啟動需要通過BootStrap ClassLoader來創建一個初始類來完成。
Extensions ClassLoader 擴展類加載器,ExtClassLoader,它用于加載一些系統之外的額外功能。
Application ClassLoader 應用類加載器,也稱作系統類加載器,它可以通過getSystemClassLoader拿到,也可以稱為系統類加載器。
Custom ClassLoader 自定義類加載器,我們可以通過繼承java.lang.ClassLoader類
來實現自己的類加載器,注意,ExtClassLoader和AppClassLoader也是繼承自java.lang.ClassLoader類
的。
總體的加載關系是:null->ExtClassloader->AppClassLoader,需要注意的是,BootStrap ClassLoader無法在Java中通過.class.getClassLoader()訪問它的父加載器。
同時,這只代表加載關系,而不代表繼承關系,ClassLoader的繼承關系如下:
其中:
ClassLoader本身是一個抽象類,定義了主要的一些功能。
SecureClassLoader擴展實現了ClassLoader方面加入權限方面的功能,加強了ClassLoader的安全性。
URLClassLoader通過URL路徑,從Jar文件和文件夾下加載類和資源。
ExtClassLoader和AppClassLoader,本身都是Launcher應用的內部類,Launcher是Java虛擬機的入口應用,二者都在Launcher中初始化;
雙親委派機制,大致意思就是當一個類加載器去加載某個類時,會優先委托給父加載器去進行加載,而不是自己加載。這樣能夠有效地保護加載類的安全性,比如我們希望加載一個
java.lang.String
類,在雙親委派機制下,我們就會優先由父類進行加載,而不是我們自己的類加載器去做加載,父類加載器會從指定的,安全的目錄下查找String類。如果是我們自己的類加載器中加載該類那么可能會出現一些安全方面的問題。換句話說,你自己定義的java.lang.String
類是無法被加載的。當然,這個前提是,你得遵循雙親委派機制,如果你重新寫個類加載器,自定義了loadClass并且不遵循雙親委派機制,那么雙親委派機制就被打破了。這也是為什么,JVM認為兩個類相等不僅僅要類名相等,而且要類加載器相等才是同一個類。
區別于標準JVM以Class文件為輸入的字節碼文件,Android虛擬機采用更為緊湊的Dex文件作為輸入文件,無論是Dalvik VM還是ART VM。這樣一來,類加載器自然也會有所改變。
Android中的類加載器類型被分為兩類:
系統類加載器:PathClassLoader、DexClassLoader、BootClassLoader
自定義類加載器
Android 系統啟動時,會使用BootClassLoader來加載常用的類,與SDK中的BootStrap ClassLoader不同的是,它本身不是由C/C++實現的, 而是采用Java實現的,它是ClassLoader的內部類,繼承自ClassLoader,本身是一個單例類。應用中無法直接訪問到BootClassLoader。
BootClassLoader是在ZygoteInit的入口方法中,間接調用了preloadClasses方法中,進行創建的,Android在/framework/base/preloaded-classes
中封裝了一系列的預加載類的目錄,一些常用類,例如:ContextImpl、Fragment、Dialog等等都在列。預加載之后,應用程序啟動時,就不用額外去做加載了。
PathClassLoader它通常被系統用來加載Apk中自帶的Dex文件,它的構造函數中少了一個參數:optimizedDirectory,這是因為PathClassLoader定義了默認的optimizedDirectory參數:/data/dalvik-cache/
,因此,我們無法自定義Dex文件的解壓路徑,所以我們加載類時,一般都使用DexClassLoader。
PathClassLoader是Zygote進程在fork SystemServer進程時創建的,當Zygote進程在新創建SystemServer時,通過調用forkSystemServer方法時,會調用到handleSystemServerProcess(),然后調用createPathClassLoader()去創建PathClassLoader。
可以在磁盤中加載.dex或者是.apk文件,但是本質上都是加載屬于Android 的字節碼文件:Dex文件。
它的構造方法有四個參數:
dexPath:相關的文件路徑;支持多個路徑,使用「:」分割
optimizedDirectory:Dex文件解壓后的文件存儲路徑,一般情況下使用當前文件的私有路徑。
"this parameter is deprecated and has no effect since API level 26."
注意:optimizedDirectory參數在API26之后被廢棄了
librarySearchPath:包含C/C++庫的路徑集合,可以為null
parent:父加載器
我們通常在App啟動時,我們通常使用DexClassLoader動態加載Dex的方式來實現應用程序Java代碼層面的熱修復。
Android8.0 中新增的用于加載內存中的類加載器。和PathClassLoader、DexClassLoader一樣,都是BaseDexClassLoader的實現類。
BaseDexClassLoader有三個子類:DexClassLoader、PathClassLoader、InMemoryDexClassLoader,它們三個主要任務就是:加載外部的Dex文件,獲取其中定義的類信息。同樣,Android的類加載機制也遵循雙親委派機制。
BaseDexClassLoader有一個特殊的結構:DexPathList類型的pathList ,它內部維護了一個Element類型的數組,用來存儲被加載的Dex文件信息:
private Element[] dexElements;
每當我們要使用一個類時,類加載器就會先檢索:DexPathList中的所有Dex文件,逐個遍歷,看看其中是否含有所需要的類:
Element內部有一個對象:DexFile,在調用Element#findClass時,會按照如下規則去查找:
而最終,loadClassBinaryName會調用Native代碼在本地內存上創建一個指向Dex文件的對象,這樣 ,我們知道Dex文件在內存中的引用是類加載下的DexPathList中的一個個Element。
這個Element數組可以作為一個熱修復的接入點,我們知道,類加載只會被加載一次,如果此時我們有多個Dex文件,那么Dex文件的引用在Element中會按照加載的順序排列,這樣一來,排在前面的Dex類中的Class就會被優先加載,由此我們就可以將熱修復后重新生成的Patch.dex
或Patch.apk
加載到用戶手機存儲空間當中,然后自行使用DexPathClassLoader進行加載,并通過反射,Hook掉PathClassLoader,將Patch.dex
對應的Element反射插入到其DexPathList當中去。這樣一來,加載時就會優先從Patch.dex
中加載了,原理大致如下圖。修復后加載原先出錯的類ClassE將會從Patch.dex中優先加載,而出錯的Class E由于類加載的特性,將不會被加載出來。
我們所編寫的Java代碼,使用Java自帶的編譯器編譯完成之后默認的輸出一定是.class文件,而在ART或者Dalvik虛擬機中需要輸入Dex文件,那么在其中必然存在Class -> Dex文件的過程。
該過程是由d8工具完成的,在我們的SDK目錄下:/Library/Android/sdk/build-tools/28.0.3
,有非常多和我們Android 構建相關的一些工具,例如aapt2工具會負責將res.xml中的文件在R.java中生成對應的ID引用、負責將二進制資源、資源表resources.arsc、classes.dex以及assets集成打包進一個未簽名的.apk文件內。
d8工具會將需要打包的.class文件、額外依賴的Jar文件一同參與編譯。比如我們需要對appt2下的一個MainActivity.class文件進行編譯,那么我們可以在一個新項目中,點擊Android Studio的編譯或者上面的綠色小錘子,然后在:MyApplication2/app/build/intermediates/javac/debug/classes/com/red/myapplication
下我看到MainActicity.class文件,然后在該目錄下執行如下的指令,注意,將MainActivity的AppcompatActivity換成Activity,因為前者是屬于AndroidX系列的的AAR包中的依賴,需要額外添加。
d8 aapt/MainActivity.class --lib /Library/Android/sdk/platforms/android-29/android.jar --output ./
其中,--lib
指定了一些額外的依賴,因為MainActivity中會依賴android.jar
中的一些文件(比如Activity類),完成后,我們就得到了一個classes.dex文件,結構如下:
如上的步驟都在Android提供的Gradle套件中,幫我們完成了,Gradle插件依賴的本質,就是插件文件的下載(Gradle同步)和引用。
“Android中的類文件和類加載器實例分析”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。