Android 動態加載dex

jopen 8年前發布 | 17K 次閱讀 Android Android開發 移動開發

 

首先如果僅僅是因為64K method的問題可以直接看這里 DexGuard、Proguard、Multi-dex 給出的解決方案

本文主要討論從編譯層面,dex動態加載器選擇層面以及安全層面討論dex動態加載

</div>

前言

比較兩個類是否相等: 是基于采用同樣的加載器加載的,否則必不相等。

一般加載器類別

虛擬機的角度

1. 啟動類加載器(Bootstrap ClassLoader)

使用C++語言實現,虛擬機自身的一部分

2. 其他的類加載器

使用Java語言實現,獨立于JVM外部,全部繼承自類java.lang.ClassLoader

開發人員角度

以下三種都是系統提供的供開發人員使用的加載器

</div>

1. 啟動類加載器(Bootstrap ClassLoader)

負責加載: JAVA_HOME\lib目錄中 能被JVM是被的類庫到JVM內存中(名稱不符合的類庫不會被加載)

無法被Java程序直接引用。

2. 擴展類加載器(Extersion ClassLoader)

負責加載: JAVA_HOME\lib\ext目錄中的類庫

可以被開發者直接使用

3. 應用程序類加載器(Application ClassLoader)

也稱為 系統類加載器

</div>

負責加載: 用戶類路徑(Classpath)上所指定的類庫

可以被開發者直接使用

一般是應用程序默認的類加載器

什么是雙親委派模型?

一個類收到了類加載請求,會將請求先委派給父類加載,每層皆如此,因此所有的類加載是從上而下的,只有上層無法加載了才到下層加載。

也可以參考ClassLoader中給出的解釋:

Loads the class with the specified name, optionally linking it after loading. The following steps are performed:

  1. Call findLoadedClass(java.lang.String) to determine if the requested class has already been loaded.
  2. If the class has not yet been loaded: Invoke this method on the parent class loader.
  3. If the class has still not been loaded: Call findClass(java.lang.String) to find the class.</pre>

    為什么要遵循雙親委派模型?

    為了保證所加載的類的唯一性,保證相同的類只會被一個加載器所加載。

    Dalvik虛擬機的類加載器與其他Java虛擬機的不同?

    一般的Java虛擬機,是自定義繼承自ClassLoader的類加載器,然后通過defineClass方法從二進制流中加載Class,或者從Class文件中讀取。而Dalvik虛擬機是閹割以及修改過的,無法從二進制流中加載,Dalvik只識別dex文件,因此我們能加載的只是dex文件或包含dex文件的.jar或.apk。

    I. Android 動態加載Dex的方式

    Android 動態加載dex

    DexFile

    Android中的這幾種類加載器實際是依賴DexFile的,對于DexFile有以下兩點

    1. 打開的DEX文件不會直接存儲在DexFile對象中,而是存儲在對于虛擬機只讀的memory-mapped上。
    2. 我們無法直接調用DexFile.loadClass進行對dex的加載,只能通過ClassLoader進行加載

    PathClassLoader的使用案例推薦參考: secondary-dex-gradle/…/secondarydex/plugin/

    DexClassLoader的使用案例推薦參考: Custom Class Loading in Dalvik ,如果你有網絡下載dex動態打補丁的需求的話

    II. 何在編譯層面實現打指定獨立dex

    Ant

    可以參考 這里 后面的Build Process.

    Gradle

    在編譯層面將指定的module拆分出來打包成dex放入assets中,完全可以參考這個方案:

    secondary-dex-gradle/app/build.gradle

    如果不理解的可以看我fork的,我添加了中文注解: Jacksgong/secondary-dex-gradle/app/build.gradle

    III. 安全性討論

    動態加載Dex的安全性主要存在兩方面

    1. 存儲dex的文件暴露在其他應用可讀寫的目錄下
    2. 加載外部dex的時候沒有做好完整的安全性校驗

    解決方案

    1. 盡量將dex放到當前應用的私有目錄下,保證只有當前應用uid可以寫甚至讀(一般就只有Context.getFileDir()/Context.getDir(String, MODE_PRIVATE)/Context.getCacheDir()),這方面目錄相關知識可以參看 Android中盡量不用Storage Permission
    2. 對從服務端下載或者外部加載的dex,做校驗(對文件進行哈希值校驗等)
    3. 將dex文件加密,通過JNI將解密代碼寫在Native層,解密之后通過defineClass指定路徑加載完成后,刪除解密后文件
 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!