日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android classloader异常,Android中ClassLoader类加载机制

發布時間:2025/3/21 Android 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android classloader异常,Android中ClassLoader类加载机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Android中apk的構建過程

構建apk

如圖 所示,典型 Android 應用模塊的構建流程通常依循下列步驟:

編譯器將您的源代碼轉換成 DEX(Dalvik Executable) 文件(其中包括 Android 設備上運行的字節碼),將所有其他內容轉換成已編譯資源。

APK 打包器將 DEX 文件和已編譯資源合并成單個 APK。 不過,必須先簽署 APK,才能將應用安裝并部署到 Android 設備上。* APK 打包器使用調試或發布密鑰庫簽署您的 APK:

如果您構建的是調試版本的應用(即專用于測試和分析的應用),打包器會使用調試密鑰庫簽署您的應用。 Android Studio 自動使用調試密鑰庫配置新項目。

如果您構建的是打算向外發布的發布版本應用,打包器會使用發布密鑰庫簽署您的應用。 要創建發布密鑰庫,請閱讀在 Android Studio 中簽署您的應用。

在生成最終 APK 之前,打包器會使用 zipalign 工具對應用進行優化,減少其在設備上運行時占用的內存。

構建流程結束時,將獲得應用的調試 APK 或發布 APK,您可使用它們進行部署、測試,或向外部用戶發布。

Android中的類加載機制

1 Android中的ClassLoader

Java中的ClassLoader是加載class文件,而Android中的虛擬機無論是dvm還是art都只能識別dex文件。因此Java中的ClassLoader在Android中不適用。Android中的java.lang.ClassLoader這個類也不同于Java中的java.lang.ClassLoader。

Android中的ClassLoader類型也可分為系統ClassLoader和自定義ClassLoader。其中系統ClassLoader包括3種分別是

BootClassLoader: Android系統啟動時會使用BootClassLoader來預加載常用類,與Java中的Bootstrap ClassLoader不同的是,它并不是由C/C++代碼實現,而是由Java實現的。BootClassLoader是ClassLoader的一個內部類。

PathClassLoader,全名是dalvik/system.PathClassLoader,可以加載已經安裝的Apk,也就是/data/app/package 下的apk文件,也可以加載/vendor/lib, /system/lib下的nativeLibrary。

DexClassLoader,全名是dalvik/system.DexClassLoader,可以加載一個未安裝的apk文件。

PathClassLoader和DexClasLoader都是繼承自 dalviksystem.BaseDexClassLoader,它們的類加載邏輯全部寫在BaseDexClassLoader中。

Android中的ClassLoader

2 ClassLoader源碼分析

在Android中我們主要關心的是PathClassLoader和DexClassLoader。

PathClassLoader用來操作本地文件系統中的文件和目錄的集合。并不會加載來源于網絡中的類。Android采用這個類加載器一般是用于加載系統類和它自己的應用類。這個應用類放置在data/data/包名下。

看一下PathClassLoader的源碼,只有2個構造方法:

package dalvik.system;

public class PathClassLoader extends BaseDexClassLoader {

public PathClassLoader(String dexPath, ClassLoader parent) {

super(dexPath, null, null, parent);

}

public PathClassLoader(String dexPath, String libraryPath,

ClassLoader parent) {

super(dexPath, null, libraryPath, parent);

}

}

DexClassLoader可以加載一個未安裝的APK,也可以加載其它包含dex文件的JAR/ZIP類型的文件。DexClassLoader需要一個對應用私有且可讀寫的文件夾來緩存優化后的class文件。而且一定要注意不要把優化后的文件存放到外部存儲上,避免使自己的應用遭受代碼注入攻擊。看一下它的源碼,只有1個構造方法:

package dalvik.system;

import java.io.File;

public class DexClassLoader extends BaseDexClassLoader {

public DexClassLoader(String dexPath, String optimizedDirectory,

String libraryPath, ClassLoader parent) {

super(dexPath, new File(optimizedDirectory), libraryPath, parent);

}

}

可以看到,PathClassLoader和DexClassLoader除了構造方法傳參不同,其它的邏輯都是一樣的。要注意的是DexClassLoader構造方法第2個參數指的是dex優化緩存路徑,這個值是不能為空的。而PathClassLoader對應的dex優化緩存路徑為null是因為Android系統自己決定了緩存路徑。

接下來我們看一下BaseDexClassLoader這個類:

dexPath,指的是在Androdi包含類和資源的jar/apk類型的文件集合,指的是包含dex文件。多個文件用“:”分隔開,用代碼就是File.pathSeparator。

optimizedDirectory,指的是odex優化文件存放的路徑,可以為null,那么就采用默認的系統路徑。

libraryPath,指的是native庫文件存放目錄,也是以“:”分隔。

parent,parent類加載器

可以看到,在BaseDexClassLoader類中初始化了DexPathList這個類的對象。這個類的作用是存放指明包含dex文件、native庫和優化目錄。

# dalvik.system.BaseDexClassLoader

public BaseDexClassLoader(String dexPath, File optimizedDirectory,

String libraryPath, ClassLoader parent) {

super(parent);

this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);

}

dalvik.system.DexPathList封裝了dex路徑,是一個final類,而且訪問權限是包權限,也就是說外界不可繼承,也不可訪問這個類。

BaseDexClassLoader在其構造方法中初始化了DexPathList對象,我們來看一下DexPathList的源碼,我們需要重點關注一下它的成員變量dexElements,它是一個Element[]數組,是包含dex的文件集合。Element是DexPathList的一個靜態內部類。DexPathList的構造方法有4個參數。從其構造方法中也可以看到傳遞過來的classLoade對象和dexPath不能為null,否則就拋出空指針異常。

# dalvik.system.DexPathList

private final Element[] dexElements;

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;

ArrayList suppressedExceptions = new ArrayList();

// 通過makeDexElements方法來獲取Element數組

// splitDexPath(dexPath)方法是用來把我們之前按照“:”分隔的路徑轉為File集合。

this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,

suppressedExceptions);

if (suppressedExceptions.size() > 0) {

this.dexElementsSuppressedExceptions =

suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);

} else {

dexElementsSuppressedExceptions = null;

}

this.nativeLibraryDirectories = splitLibraryPath(libraryPath);

}

加載一個dex文件調用的是loadDexFile()方法。

# dalvik.system.DexPathList

private static DexFile loadDexFile(File file, File optimizedDirectory)

throws IOException {

// 如果緩存存放目錄為null就直接創建一個DexFile對象返回

if (optimizedDirectory == null) {

return new DexFile(file);

} else {

// 根據緩存存放目錄和文件名得到一個優化后的緩存文件路徑

String optimizedPath = optimizedPathFor(file, optimizedDirectory);

// 調用DexFile的loadDex()方法來獲取DexFile對象。

return DexFile.loadDex(file.getPath(), optimizedPath, 0);

}

}

DexFile的loadDex()方法如下,內部也做了一些調用。拋開這些細節來講,它的作用就是加載DexFile文件,而且會把優化后的dex文件緩存到對應目錄。

分析到這,我們可以小結一下:在BaseDexClassLoader對象構造方法內,創建了PathDexList對象。而在PathDexList構造方法內部,通過調用一系列方法,把直接包含或者間接包含dex的文件解壓縮并緩存優化后的dex文件,通過PathDexList的成員變量Element[] dexElements來指向這個文件。

到此我們就分析完了BaseDexClassLoader的構造方法。

類加載是按需加載,也就是說當明確需要使用class文件的時候才會加載。我們來看一下在Android中ClassLoader的loadeClass()方法。

與在Java中的loadClass()方法主要流程是類似的,不過因為Android中BootClassLoader是用Java代碼寫的,所以可以直接當作系統類加載器的parent類加載器。在Android中如果parent類加載器找不到類,最終還是會調用ClassLoader對象自己的findClass()方法。這個與在Java中邏輯是一樣的。

我們可以去看一下BaseDexClassLoader類的findClass()方法。

# dalvik.system.BaseDexClassLoader

@Override

protected Class> findClass(String name) throws ClassNotFoundException {

List suppressedExceptions = new ArrayList();

// 調用DexPathList對象的findClass()方法

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;

}

可以看到,實際上BaseDexClassLoader調用的是其成員變量DexPathList pathList的findClass()方法。

# dalvik.system.DexPathList

public Class findClass(String name, List suppressed) {

// 遍歷Element

for (Element element : dexElements) {

// 獲取DexFile,然后調用DexFile對象的loadClassBinaryName()方法來加載Class文件。

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最終還是遍歷其自身的Element[]數組,獲取DexFile對象來加載Class文件。我們之前講DexPathList構造方法內是調用其makeDexElements()方法來創建Element[]數組的,而且也提到了如果zip文件或者dex文件二者之一不為null,就把元素添加進來,而添加進來的zip存在不為null也不包含dex文件的可能。從上面的代碼中也可以看到,獲取Class的時候跟這個zip文件沒什么關系,調用的是dex文件對應的DexFile的方法來獲取Class。

數組的遍歷是有序的,假設有兩個dex文件存放了二進制名稱相同的Class,類加載器肯定就會加載在放在數組前面的dex文件中的Class。現在很多熱修復技術就是把修復的dex文件放在DexPathList中Element[]數組的前面,這樣就實現了修復后的Class搶先加載了,達到了修改bug的目的。

Android加載一個Class是調用DexFile的defineClass()方法。而不是調用ClassLoader的defineClass()方法。這一點與Java不同,畢竟Android虛擬機加載的dex文件,而不是class文件。

總結

Android中的類加載器是BootClassLoader、PathClassLoader、DexClassLoader,其中BootClassLoader是虛擬機加載系統類需要用到的,PathClassLoader是App加載自身dex文件中的類用到的,DexClassLoader可以加載直接或間接包含dex文件的文件,如APK等。

PathClassLoader和DexClassLoader都繼承自BaseDexClassLoader,它的一個DexPathList類型的成員變量pathList很重要。DexPathList中有一個Element類型的數組dexElements,這個數組中存放了包含dex文件(對應的是DexFile)的元素。BaseDexClassLoader加載一個類,最后調用的是DexFile的方法進行加載的。

無論是熱修復還是插件化技術中都利用了類加載機制,所以深入理解Android中的類加載機制對于理解這些技術的原理很有幫助

總結

以上是生活随笔為你收集整理的android classloader异常,Android中ClassLoader类加载机制的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。