DEX 文件檔
- 虛擬器會在應用程式啟動時,動態的從儲存裝置讀取並解壓縮個別的 Classes 到記憶體中,在沒有經過適度優化的架構下,每個獨立的虛擬器行程,都會包含自己一份的相關 Classes 記憶體空間,而這些 Classes 就儲存在 dex 檔案中
- dex 檔案會以唯讀方案載入到記憶體中,並跨行程共享
- 可以把多個 Classes 檔案整合到一個 dex 檔案中
- 通常儲存於 apk 中,解壓縮 apk 檔後,即可以得到 classes.dex 文件檔
- 因為 Android 的虛擬機 (Dalvik VM) 是不認識 Java 打出 jar 的 byte code,需要通过 dex 工具來優化轉換成 Dalvik byte code 才行。在 apk 中可以看出:引入其他 Jar 的内容都被打包進了 classes.dex
為何要動態載入 DEX 文件檔
- 當專案日益龐大,所產生的 dex 文件也會隨著變大。但在 Android 2.3 或以下版本中,限制 DEX 文件檔案最大為 5MB,再較新的版本則支援 8 或 16MB。如果您的 app 需支援 Android 2.3 或以下版本,且 DEX 超過 5MB,會出現 LinearAlloc exceeded capacity Problem,此時就需要將 DEX 檔案切割並動態載入
- DEX 檔案可跨行程共享,所以如果知道 DEX 中所含的 class 與 method 的話,其實也可以跨 app 共享
如何產生 DEX 文件檔
- 先產生 JAR file:
(1) 對專案右鍵 → Export → Java → JAR file
(2) 選擇要輸出的 java file,並選擇儲存位置與檔名
(3) 下一步到底,產出 JAR file - 將 JAR file 優化換成 Dalvik byte code
(1) 需使用 ADT 中所提供的 Ant 腳本來打包,版本為 Android SDK 12,可直接下載 platform-tools 與 tools 並解壓到對應目錄
(2) 將 JAR file 拷貝到 SDK 安装目錄 android-sdk-windows\platform-tools 下,從 DOS 進入這個目錄,執行
※ 將 JAR 優化時應該重新打成 JAR (jar → dex → jar),上述指令就是把 TestDex.jar 里面的 .class 文件優化成 .dex 文件然後又打包成 test.jardx --dex --output=test.jar TestDex.jar //dx --dex --output=輸出 輸入
※ 無法直接加載 dex 文件會出現 "unable to open 'dex' file" Error - 若 Android SDK Build-tools 版本為 19 的,請降版至 18.1,否則編譯時會出現
Unable to execute dex: java.nio.BufferOverflowException
- 將優化過後的 JAR file 複製到手機中,接著就可以準備 LoadClass
※ .dex文件在 4.1 之後,Google 基於安全考量在 DexFile 函數中增加一個驗證文件歸屬權的步骤,故如果直接使用 Environment.getExternalStorageDirectory() 讀取成 file 會出現
剩至會出現 NoClassDefFoundError (本篇範例主要是提供 android 2.3 或以下系統解決 LinearAlloc 問題,故暫不考慮 android 4.X 版系統的運行結果,android 4.X 版系統可透過 getDir 來取得 file)java.lang.IllegalArgumentException: Optimized data directory /storage/sdcard0 is not owned by the current user. Shared storage cannot protect your application from code injection attacks.
※ res目錄下檔案,AndroidManifest 不須打包成 dex 文件,因為打包成 apk 時就會包含
動態載入 dex 中 class
※ 動態載入 class 有兩種方法:
(1) PathClassLoader:加載路徑必須在 /data/app 路徑下
(2) DexClassLoader:加載 sdcard 目錄下的 apk 或 jar 文件
這裡示範使用 DexClassLoader 來載入 class
(1) PathClassLoader:加載路徑必須在 /data/app 路徑下
(2) DexClassLoader:加載 sdcard 目錄下的 apk 或 jar 文件
這裡示範使用 DexClassLoader 來載入 class
//載入手機中優化過的 JAR
final File optimizedDexOutputPath = new File(Environment.getExternalStorageDirectory().toString() + File.separator + "wasync.jar");
//載入 JAR 中的 class
DexClassLoader cl = new DexClassLoader(optimizedDexOutputPath.getAbsolutePath(),
Environment.getExternalStorageDirectory().toString(),
null,
KoKoLaApplication.getInstance().getClassLoader());
Class libProviderClazz = null;
try {
libProviderClazz = cl.loadClass("org.slf4j.LoggerFactory"); //class 完整名稱
Method getInstance = libProviderClazz.getDeclaredMethod("getLogger", String.class); //呼叫 class 中 method
getInstance.setAccessible(true); //設定 method 是否可訪問,不加會出現 access to method denied
Object logger = getInstance.invoke(null, classname); //第一個参数為 null 表示此為靜態方法
return logger;
} catch (Exception exception) {
exception.printStackTrace();
}
應用範圍:
app 連動程式,例如:line 遊戲連動,可直接載入 line 使用者資料,不需再連網路下載使用者資訊,減少使用者等待時間
沒有留言:
張貼留言