国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

插件化知識詳細分解及原理 之ClassLoader及dex加載過程

2019-11-06 10:03:25
字體:
來源:轉載
供稿:網友

上一篇插件化知識詳細分解及原理 之代理,hook,反射,接著上篇我們這一篇說ClassLoader及dex加載過程。

為了解決65535這個問題,Google提出了multidex方案,即一個apk文件可以包含多個dex文件。 不過值得注意的是,除了第一個dex文件以外,其他的dex文件都是以資源的形式被加載的, 換句話說就是在application初始化前將dex文件注入到系統的ClassLoader中的。 根據Android虛擬機的類加載機制,同一個類只會被加載一次,所以熱修復也使用了這樣的機制,要讓修復后的類替換原有的類就必須讓補丁包的類被優先加載,也就是插入到原有dex之前。

首先需要知道的是android中兩個主要的Classloader,PathClassLoader和DexClassLoader, 它們都繼承自BaseDexClassLoader,這兩個類有什么區別呢?其實看一下它們的源碼注釋就一目了然了。

Android系統通過PathClassLoader來加載系統類和主dex中的類。 而DexClassLoader則用于加載其他dex文件中的類。 他們都是繼承自BaseDexClassLoader,具體的加載方法是findClass。

為什么說PathClassLoader只能加載系統類和主dex的類呢,我們看一下這個類的源代碼 所有ClassLoader的源碼都在/libcore/dalvik/src/main/java/dalvik/system/目錄下

PathClassLoader: /** * PRovides a simple {@link ClassLoader} implementation that Operates on a list * of files and directories in the local file system, but does not attempt to * load classes from the network. Android uses this class for its system class * loader and for its application class loader(s). */ 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 libraryPath 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 libraryPath, ClassLoader parent) { super(dexPath, null, libraryPath, parent); } }

我們翻譯一下他的注釋,大概的意思是說 PathClassLoader被用來加載本地文件系統上的文件或目錄,但不能從網絡上加載, 關鍵是它被用來加載系統類和我們的應用程序,這也是為什么它的兩個構造函數中調用父類構造器的時候第二個參數傳null, 那第二個參數代表什么呢我們看一下DexClassLoader的源碼

DexClassLoader: /** * A class loader that loads classes from {@code .jar} and {@code .apk} files * containing a {@code classes.dex} entry. This can be used to execute code not * installed as part of an application. * * <p>This class loader requires an application-private, writable directory to * cache optimized classes. Use {@code Context.getDir(String, int)} to create * such a directory: <pre> {@code * File dexOutputDir = context.getDir("dex", 0); * }</pre> * * <p><strong>Do not cache optimized classes on external storage.</strong> * External storage does not provide access controls necessary to protect your * application from code injection attacks. */ 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 libraryPath 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 libraryPath, ClassLoader parent) { super(dexPath, new File(optimizedDirectory), libraryPath, parent); } }

看一下構造上的注釋,參數解釋如下:

dexPath: 需要被加載的dex文件地址,可以多個,用File.pathSeparator分割

optimizedDirectory: dex文件被加載后會被編譯器優化,優化之后的dex存放路徑, 不可以為null。注意,注釋中也提到需要一個應用私有的可寫的一個路徑, 以防止應用被注入攻擊,并且給出了例子 File dexOutputDir = context.getDir(“dex”, 0);

libraryPath:包含libraries的目錄列表,同樣用File.pathSeparator分割,如果沒有則傳null就行了

parent:父類構造器

到這里估計會問不是說了optimizedDirectory不能傳null嗎,那PathClassLoader怎么傳了null呢, 我們繼續看一下BaseClassLoader的findClass,

public class BaseDexClassLoader extends ClassLoader { private final DexPathList pathList; public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) { super(parent); this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { List<Throwable> suppressedExceptions = new ArrayList<Throwable>(); Class c = pathList.findClass(name, suppressedExceptions); if (c == null) { ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class /"" + name + "/" on path: " + pathList); for (Throwable t : suppressedExceptions) { cnfe.addSuppressed(t); } throw cnfe; } return c; } }

構造方法中創建了一個叫pathList的DexPathList類型的對象,然后在findClass的時候 代碼轉移到了pathList的findClass中,DexPathList又是什么呢我們進去看一下。

/** * Constructs an instance. * * @param definingContext the context in which any as-yet unresolved * classes should be defined * @param dexPath list of dex/resource path elements, separated by * {@code File.pathSeparator} * @param libraryPath list of native library directory path elements, * separated by {@code File.pathSeparator} * @param optimizedDirectory directory where optimized {@code .dex} files * should be found and written to, or {@code null} to use the default * system directory for same */ public DexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory) { if (definingContext == null) { throw new NullPointerException("definingContext == null"); } if (dexPath == null) { throw new NullPointerException("dexPath == null"); } if (optimizedDirectory != null) { if (!optimizedDirectory.exists()) { throw new IllegalArgumentException( "optimizedDirectory doesn't exist: " + optimizedDirectory); } if (!(optimizedDirectory.canRead() && optimizedDirectory.canWrite())) { throw new IllegalArgumentException( "optimizedDirectory not readable/writable: " + optimizedDirectory); } } this.definingContext = definingContext; this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory); this.nativeLibraryDirectories = splitLibraryPath(libraryPath); }

我們先翻一下一下注釋,第四個參數說optimizedDirectory 如果為空, 那么使用系統默認的文件夾,為什么PathClassLoader傳空就行呢,其實是 因為我們的應用已經安裝并優化了,優化后的dex存在于/data/dalvik-cache目錄下,這就是系統默認的文件夾, 這就是說為什么我們只能用DexClassLoader去加載其他類了

我們接著看一下最后兩句代碼

this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory); this.nativeLibraryDirectories = splitLibraryPath(libraryPath);

賦值了一個dexElements的數組,這個其實就是存放我們dex文件的數組, nativeLibraryDirectories 就是lib庫了,我們看一下makeDexElements方法

/** * Makes an array of dex/resource path elements, one per element of * the given array. */ private static Element[] makePathElements(List<File> files, File optimizedDirectory, List<IOException> suppressedExceptions) { List<Element> elements = new ArrayList<>(); /* * Open all files and load the (direct or contained) dex files * up front. */ for (File file : files) { File zip = null; File dir = new File(""); DexFile dex = null; String path = file.getPath(); String name = file.getName(); if (path.contains(zipSeparator)) { String split[] = path.split(zipSeparator, 2); zip = new File(split[0]); dir = new File(split[1]); } else if (file.isDirectory()) { // We support directories for looking up resources and native libraries. // Looking up resources in directories is useful for running libcore tests. elements.add(new Element(file, true, null, null)); } else if (file.isFile()) { if (name.endsWith(DEX_SUFFIX)) { // Raw dex file (not inside a zip/jar). try { dex = loadDexFile(file, optimizedDirectory); } catch (IOException ex) { System.logE("Unable to load dex file: " + file, ex); } } else { zip = file; try { dex = loadDexFile(file, optimizedDirectory); } catch (IOException suppressed) { /* * IOException might get thrown "legitimately" by the DexFile constructor if * the zip file turns out to be resource-only (that is, no classes.dex file * in it). * Let dex == null and hang on to the exception to add the tea-leaves for * when findClass returns null. */ suppressedExceptions.add(suppressed); } } } else { System.logW("ClassLoader referenced unknown path: " + file); } if ((zip != null) || (dex != null)) { elements.add(new Element(dir, false, zip, dex)); } } return elements.toArray(new Element[elements.size()]); }

方法將加載dex文件,并創建Element類型加入到elements數組中,我們再看findClass方法,因為在BaseClassLoader的findClass方法調用了DexPathList的findClass方法

public Class findClass(String name, List<Throwable> suppressed) { for (Element element : dexElements) { DexFile dex = element.dexFile; if (dex != null) { Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed); if (clazz != null) { return clazz; } } } if (dexElementsSuppressedExceptions != null) { suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions)); } return null;}

DexPathList的findClass也很簡單,dexElements是維護dex文件的數組, 每一個item對應一個dex文件。DexPathList遍歷dexElements,從每一個dex文件中查找目標類,在找到后即返回并停止遍歷。

好了,我們的dex大概加載過程就很清楚了。本篇說的內容并沒有什么技術含量,只是順著一條主線梳理了下整個過程,對于搞明白一些細節可能還是有點幫助。下一篇我們說應用的啟動過程。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 江西省| 泰来县| 阿合奇县| 六盘水市| 永昌县| 新邵县| 陆河县| 东台市| 东莞市| 财经| 德兴市| 札达县| 合作市| 高雄市| 昭苏县| 潼关县| 合川市| 罗平县| 栖霞市| 霸州市| 安西县| 桐庐县| 合阳县| 南宫市| 安康市| 阳高县| 平邑县| 怀集县| 泸水县| 芷江| 富源县| 寻乌县| 孝昌县| 东阿县| 二手房| 镇坪县| 达孜县| 白银市| 鸡西市| 阜阳市| 通山县|