本篇文章給大家分享的是有關怎么在Android中利用ClassLoader對類進行加載,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
Java的類加載設計了一套雙親代理的模式,使得用戶沒法替換系統的核心類,從而讓應用更安全。所謂雙親代理就是指,當加載類的時候首先去Bootstrap中加載類,如果沒有則去Extension中加載,如果再沒有才去AppClassLoader中去加載。從而實現安全和穩定。
Java ClassLoader
BootstrapClassLoader
引導類加載器 ,用來加載Java的核心庫。通過底層代碼來實現的,基本上只要parent為null,那就表示引導類加載器。
比如: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
系統類加載器 (不要被名字給迷惑),用來加載Java應用中的類。一般來說自己寫的類都是通過這個加載的。而Java中 ClassLoader.getSystemClassLoader() 返回的就是AppClassLoader。(Android中修改了ClassLoader的邏輯,返回的會是一個PathClassLoader)
自定義ClassLoader
用戶如果想自定義ClassLoader的話,只需要繼承自 java.lang.ClassLoader 即可。
ClassLoader中與加載類相關的方法:
也許你不太了解上面幾個函數的區別,沒關系,我們來看下源碼是如何實現的。
//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; }
所以優先級大概如下:
loadClass → findLoadedClass → parent.loadClass/findBootstrapClassOrNull → findClass → defineClass
Android ClassLoader
在Android中ClassLoader主要有兩個直接子類,叫做 BaseDexClassLoader 和 SecureClassLoader 。而前者有兩個直接子類是 PathClassLoader 和 DexClassLoader (Android O添加了 InMemoryDexClassLoader ,略)。
我們只討論PathClassLoader和DexClassLoader
PathClassLoader
用來加載安裝了的應用中的dex文件。它也是Android里面的一個最核心的ClassLoader了。相當于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); } }
它的實例化是通過調用 ApplicationLoaders.getClassLoader
來實現的。
它是在ActivityThread啟動時發送一個BIND_APPLICATION消息后在handleBindApplication中創建ContextImpl時調用LoadedApk里面的 getResources(ActivityThread mainThread)
最后回到ActivityThread中又調用LoadedApk的 getClassLoader
生成的,具體的在LoadedApk的 createOrUpdateClassLoaderLocked
。
那么問題來了,當Android加載class的時候,LoadedApk中的ClassLoader是怎么被調用到的呢?
其實Class里面,如果你不給ClassLoader的話,它默認會去拿Java虛擬機棧里面的 CallingClassLoader ,而這個就是LoadedApk里面的同一個ClassLoader。
//Class.java public static Class<?> forName(String className) throws ClassNotFoundException { return forName(className, true, VMStack.getCallingClassLoader()); }
查看VMStack的源碼發現 getCallingClassLoader
其實是一個native函數,Android通過底層實現了這個。
//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); } }
比如現在很流行的插件化/熱補丁,其實都是通過DexClassLoader來實現的。具體思路是: 創建一個DexClassLoader,通過反射將前者的DexPathList跟系統的PathClassLoader中的DexPathList合并,就可以實現優先加載我們自己的新類,從而替換舊類中的邏輯了。
以上就是怎么在Android中利用ClassLoader對類進行加載,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。