詳解Android類加載ClassLoader
基本知識
Java的類加載設(shè)計了一套雙親代理的模式,使得用戶沒法替換系統(tǒng)的核心類,從而讓應(yīng)用更安全。所謂雙親代理就是指,當(dāng)加載類的時候首先去Bootstrap中加載類,如果沒有則去Extension中加載,如果再沒有才去AppClassLoader中去加載。從而實現(xiàn)安全和穩(wěn)定。
Java ClassLoader
BootstrapClassLoader
引導(dǎo)類加載器 ,用來加載Java的核心庫。通過底層代碼來實現(xiàn)的,基本上只要parent為null,那就表示引導(dǎo)類加載器。
比如:charsets.jar、deploy.jar、javaws.jar、jce.jar、jfr.jar、jfxswt.jar、jsse.jar、management-agent.jar、plugin.jar、resources.jar、rt.jar
ExtClassLoader
拓展類加載器 ,用來加載Java的拓展的類庫, ${JAVA_HOME}/jre/lib/ext/ 目錄中的所有jar。
比如:cldrdata.jar、dnsns.jar、jfxrt.jar、localedata.jar、nashorn.jar、sunec.jar、sunjce_provider.jar、sunpkcs11.jar、zipfs.jar等等
AppClassLoader
系統(tǒng)類加載器 (不要被名字給迷惑),用來加載Java應(yīng)用中的類。一般來說自己寫的類都是通過這個加載的。而Java中 ClassLoader.getSystemClassLoader() 返回的就是AppClassLoader。(Android中修改了ClassLoader的邏輯,返回的會是一個PathClassLoader)
自定義ClassLoader
用戶如果想自定義ClassLoader的話,只需要繼承自 java.lang.ClassLoader 即可。
ClassLoader中與加載類相關(guān)的方法:
- getParent() 返回該類加載器的父類加載器。
- loadClass(String name) 加載名稱為 name的類,返回的結(jié)果是 java.lang.Class類的實例。
- findClass(String name) 查找名稱為 name的類,返回的結(jié)果是 java.lang.Class類的實例。
- findLoadedClass(String name) 查找名稱為 name的已經(jīng)被加載過的類,返回的結(jié)果是 java.lang.Class類的實例。
- defineClass(String name, byte[] b, int off, int len) 把字節(jié)數(shù)組 b中的內(nèi)容轉(zhuǎn)換成 Java 類,返回的結(jié)果是 java.lang.Class類的實例。這個方法被聲明為 final的。
也許你不太了解上面幾個函數(shù)的區(qū)別,沒關(guān)系,我們來看下源碼是如何實現(xiàn)的。
//ClassLoader.java protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats } } return c; }
所以優(yōu)先級大概如下:
loadClass → findLoadedClass → parent.loadClass/findBootstrapClassOrNull → findClass → defineClass
Android ClassLoader
在Android中ClassLoader主要有兩個直接子類,叫做 BaseDexClassLoader 和 SecureClassLoader 。而前者有兩個直接子類是 PathClassLoader 和 DexClassLoader (Android O添加了 InMemoryDexClassLoader ,略)。
我們只討論PathClassLoader和DexClassLoader
PathClassLoader
用來加載安裝了的應(yīng)用中的dex文件。它也是Android里面的一個最核心的ClassLoader了。相當(dāng)于Java中的那個AppClassLoader。
public class PathClassLoader extends BaseDexClassLoader { /** * Creates a {@code PathClassLoader} that operates on a given list of files * and directories. This method is equivalent to calling * {@link #PathClassLoader(String, String, ClassLoader)} with a * {@code null} value for the second argument (see description there). * * @param dexPath the list of jar/apk files containing classes and * resources, delimited by {@code File.pathSeparator}, which * defaults to {@code ":"} on Android * @param parent the parent class loader */ public PathClassLoader(String dexPath, ClassLoader parent) { super(dexPath, null, null, parent); } /** * Creates a {@code PathClassLoader} that operates on two given * lists of files and directories. The entries of the first list * should be one of the following: * * <ul> * <li>JAR/ZIP/APK files, possibly containing a "classes.dex" file as * well as arbitrary resources. * <li>Raw ".dex" files (not inside a zip file). * </ul> * * The entries of the second list should be directories containing * native library files. * * @param dexPath the list of jar/apk files containing classes and * resources, delimited by {@code File.pathSeparator}, which * defaults to {@code ":"} on Android * @param librarySearchPath the list of directories containing native * libraries, delimited by {@code File.pathSeparator}; may be * {@code null} * @param parent the parent class loader */ public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) { super(dexPath, null, librarySearchPath, parent); } }
它的實例化是通過調(diào)用 ApplicationLoaders.getClassLoader
來實現(xiàn)的。
它是在ActivityThread啟動時發(fā)送一個BIND_APPLICATION消息后在handleBindApplication中創(chuàng)建ContextImpl時調(diào)用LoadedApk里面的 getResources(ActivityThread mainThread)
最后回到ActivityThread中又調(diào)用LoadedApk的 getClassLoader
生成的,具體的在LoadedApk的 createOrUpdateClassLoaderLocked
。
那么問題來了,當(dāng)Android加載class的時候,LoadedApk中的ClassLoader是怎么被調(diào)用到的呢?
其實Class里面,如果你不給ClassLoader的話,它默認會去拿Java虛擬機棧里面的 CallingClassLoader ,而這個就是LoadedApk里面的同一個ClassLoader。
//Class.java public static Class<?> forName(String className) throws ClassNotFoundException { return forName(className, true, VMStack.getCallingClassLoader()); }
查看VMStack的源碼發(fā)現(xiàn) getCallingClassLoader
其實是一個native函數(shù),Android通過底層實現(xiàn)了這個。
//dalvik.system.VMStack /** * Returns the defining class loader of the caller's caller. * * @return the requested class loader, or {@code null} if this is the * bootstrap class loader. */ @FastNative native public static ClassLoader getCallingClassLoader();
底層想必最終也是拿到LoadedApk里面的ClassLoader。
DexClassLoader
它是一個可以用來加載包含dex文件的jar或者apk文件的,但是它可以用來加載非安裝的apk。比如加載sdcard上面的,或者NetWork的。
public class DexClassLoader extends BaseDexClassLoader { /** * Creates a {@code DexClassLoader} that finds interpreted and native * code. Interpreted classes are found in a set of DEX files contained * in Jar or APK files. * * <p>The path lists are separated using the character specified by the * {@code path.separator} system property, which defaults to {@code :}. * * @param dexPath the list of jar/apk files containing classes and * resources, delimited by {@code File.pathSeparator}, which * defaults to {@code ":"} on Android * @param optimizedDirectory directory where optimized dex files * should be written; must not be {@code null} * @param librarySearchPath the list of directories containing native * libraries, delimited by {@code File.pathSeparator}; may be * {@code null} * @param parent the parent class loader */ public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) { super(dexPath, new File(optimizedDirectory), librarySearchPath, parent); } }
比如現(xiàn)在很流行的插件化/熱補丁,其實都是通過DexClassLoader來實現(xiàn)的。具體思路是: 創(chuàng)建一個DexClassLoader,通過反射將前者的DexPathList跟系統(tǒng)的PathClassLoader中的DexPathList合并,就可以實現(xiàn)優(yōu)先加載我們自己的新類,從而替換舊類中的邏輯了。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android自定義ViewGroup實現(xiàn)淘寶商品詳情頁
這篇文章主要為大家詳細介紹了Android自定義ViewGroup實現(xiàn)淘寶商品詳情頁,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-10-10Gradle屬性設(shè)置及環(huán)境變量全面教程
這篇文章主要為大家介紹了Gradle屬性設(shè)置及環(huán)境變量的全面教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06Android中HttpURLConnection與HttpClient的使用與封裝
這篇文章主要介紹了Android中HttpURLConnection與HttpClient的使用以及封裝方法,感興趣的小伙伴們可以參考一下2016-03-03Android內(nèi)核源碼 在Ubuntu上下載,編譯,安裝
本文主要介紹Android內(nèi)核源碼,想學(xué)習(xí)深入研究Android的朋友肯定要看看Android內(nèi)核知識的,這里對下載Android內(nèi)核源代碼的下載,安裝,編譯做了詳細的介紹,有興趣的小伙伴可以參考下2016-08-08Android PraiseTextView實現(xiàn)朋友圈點贊功能
這篇文章主要為大家詳細介紹了PraiseTextView簡單實現(xiàn)朋友圈點贊功能的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-01-01Android自定義View實現(xiàn)仿1號店垂直滾動廣告條代碼
這篇文章主要介紹了Android自定義View實現(xiàn)仿1號店垂直滾動廣告條代碼,實現(xiàn)步驟及實現(xiàn)原理本文給大家介紹的非常詳細,需要的朋友參考下吧2017-01-01Android自定義ViewFlipper實現(xiàn)滾動效果
這篇文章主要為大家詳細介紹了Android自定義ViewFlipper實現(xiàn)滾動效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-08-08Android Studio下添加assets目錄的實現(xiàn)方法
下面小編就為大家?guī)硪黄狝ndroid Studio下添加assets目錄的實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-03-03