Android動態載入jar/dex
轉:http://www.cnblogs.com/over140/archive/2011/11/23/2259367.html
前言
在目前的軟硬體環境下,Native App與Web App在使用者體驗上有著明顯的優勢,但在實際專案中有些會因為業務的頻繁變更而頻繁的升級客戶端,造成較差的使用者體驗,而這也恰恰是Web App的優勢。本文對網上Android動態載入jar的資料進行梳理和實踐在這裡與大家一起分享,試圖改善頻繁升級這一弊病。
宣告
歡迎轉載,但請保留文章原始出處:)
部落格園:http://www.cnblogs.com
農民伯伯: http://over140.cnblogs.comAndroid中文翻譯組:http://androidbox.sinaapp.com/
正文
一、 基本概念和注意點
1.1 首先需要了解一點:在Android中可以動態載入,但無法像Java中那樣方便動態載入jar
原因:Android的虛擬機器(Dalvik VM)是不認識Java打出jar的byte code,需要通過dx工具來優化轉換成Dalvik byte code才行。這一點在我們們Android專案打包的apk中可以看出:引入其他Jar的內容都被打包進了classes.dex。
所以這條路不通,請大家注意。
1.2 當前哪些API可用於動態載入
1.2.1 DexClassLoader
這個可以載入jar/apk/dex,也可以從SD卡中載入,也是本文的重點。
1.2.3 PathClassLoader
只能載入已經安裝到Android系統中的apk檔案。
二、 準備
本文主要參考"四、參考文章"中第一篇文章,補充細節和實踐過程。
2.1 下載開源專案
http://code.google.com/p/goodev-demo
將專案匯入工程,工程報錯的話應該是少了gen資料夾,手動新增即可。注意這個例子是從網上下載優化好的jar(已經優化成dex然後再打包成的jar)到本地檔案系統,然後再從本地檔案系統載入並呼叫的。本文則直接改成從SD卡載入。
三、實踐
3.1 編寫介面和實現
3.1.1 介面IDynamic
package com.dynamic;
public interface IDynamic {
public String helloWorld();
}
3.1.2 實現類DynamicTest
package com.dynamic;
public class DynamicTest implements IDynamic {
@Override
public String helloWorld() {
return "Hello World!";
}
}
3.2 打包並轉成dex
3.2.1 選中工程,常規流程匯出即可,如圖:
注意:在實踐中發現,自己新建一個Java工程然後匯出jar是無法使用的,這一點大家可以根據文章一來了解相關原因,也是本文的重點之一。這裡打包匯出為dynamic.jar
(後期修復:打包請不要把介面檔案打進來,參見文章末尾後續維護!)
3.2.2 將打包好的jar拷貝到SDK安裝目錄android-sdk-windows\platform-tools下,DOS進入這個目錄,執行命名:
dx --dex --output=test.jar dynamic.jar
3.3 修改呼叫例子
修改MainActivity,如下:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mToastButton = (Button) findViewById(R.id.toast_button);
// Before the secondary dex file can be processed by the DexClassLoader,
// it has to be first copied from asset resource to a storage location.
// final File dexInternalStoragePath = new File(getDir("dex", Context.MODE_PRIVATE),SECONDARY_DEX_NAME);
// if (!dexInternalStoragePath.exists()) {
// mProgressDialog = ProgressDialog.show(this,
// getResources().getString(R.string.diag_title),
// getResources().getString(R.string.diag_message), true, false);
// // Perform the file copying in an AsyncTask.
// // 從網路下載需要的dex檔案
// (new PrepareDexTask()).execute(dexInternalStoragePath);
// } else {
// mToastButton.setEnabled(true);
// }
mToastButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
// Internal storage where the DexClassLoader writes the optimized dex file to.
//final File optimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE);
final File optimizedDexOutputPath = new File(Environment.getExternalStorageDirectory().toString()
+ File.separator + "test.jar");
// Initialize the class loader with the secondary dex file.
// DexClassLoader cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),
// optimizedDexOutputPath.getAbsolutePath(),
// null,
// getClassLoader());
DexClassLoader cl = new DexClassLoader(optimizedDexOutputPath.getAbsolutePath(),
Environment.getExternalStorageDirectory().toString(), null, getClassLoader());
Class libProviderClazz = null;
try {
// Load the library class from the class loader.
// 載入從網路上下載的類
// libProviderClazz = cl.loadClass("com.example.dex.lib.LibraryProvider");
libProviderClazz = cl.loadClass("com.dynamic.DynamicTest");
// Cast the return object to the library interface so that the
// caller can directly invoke methods in the interface.
// Alternatively, the caller can invoke methods through reflection,
// which is more verbose and slow.
//LibraryInterface lib = (LibraryInterface) libProviderClazz.newInstance();
IDynamic lib = (IDynamic)libProviderClazz.newInstance();
// Display the toast!
//lib.showAwesomeToast(view.getContext(), "hello 世界!");
Toast.makeText(MainActivity.this, lib.helloWorld(), Toast.LENGTH_SHORT).show();
} catch (Exception exception) {
// Handle exception gracefully here.
exception.printStackTrace();
}
}
});
}
3.4 執行結果
四、參考文章
五、補充
大家可以看看DexClassLoader的API文件,裡面不提倡從SD卡載入,不安全。此外,我也正在組織翻譯組儘快把這個名稱空間下的幾個類都翻譯出來,以供大家參考。
工程下載:這裡,Dex檔案下載:這裡。大家可以直接把Dex檔案拷貝到SD卡,然後執行例子。
六、後期維護
6.1 2011-12-1 修復本文錯誤
感謝網友ppp250和liuzhaocn的反饋,基本按照評論2來修改:
6.1.1 不需要在本工程裡面匯出jar,自己新建一個Java工程然後匯出來也行。
6.1.2 匯出jar時不能帶介面檔案,否則會報以下錯:
java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation
6.1.3 將jar優化時應該重新成jar(jar->dex->jar),如果如下命令:
dx --dex --output=test.jar dynamic.jar
6.2 2012-3-29 本文升級版:
Android應用開發提高系列(4)——Android動態載入(上)——載入未安裝APK中的類
請大家參照最新的文章來做動態載入!
結束
除了翻譯組的工作和自己本職的工作以外,很難抽時間出來分享一些開發心得,但正所謂擠擠總是有的,歡迎交流!
相關文章
- 請教JBoss能不能動態載入外部jar庫JAR
- Android ART dex2oat 載入加速淺析Android
- Spring Boot 如何熱載入 jar 實現動態外掛?Spring BootJAR
- Spring Boot 如何熱載入jar實現動態外掛?Spring BootJAR
- 延遲載入 Dex 檔案
- Android native層動態載入so庫Android
- android: 動態載入碎片佈局的技巧Android
- Android 動態載入資源例項解析Android
- DLL動態庫動態載入
- Android小知識-如何載入外部dex檔案中的類Android
- Android中靜態jar庫AndroidJAR
- vue 動態載入元件Vue元件
- Java動態載入類Java
- Android資源動態載入以及相關原理分析Android
- Java 熱載入jar包JavaJAR
- Multidex(二)之 Dex 預載入優化IDE優化
- Android 開發中如何動態載入 so 庫檔案Android
- 動態載入UserControl
- OrchardCore 如何動態載入模組?
- 使用dlopen載入動態庫
- ListView動態載入資料View
- QLibrary 載入動態庫
- vue 動態載入組建Vue
- goloader - golang動態載入Golang
- 熱更新--動態載入frameworkFramework
- 動態庫載入過程
- 動態載入APK原理分享APK
- 動態載入JS和CSSJSCSS
- 動態載入JS的方法JS
- 動態載入!dom應用!
- 指令碼的動態載入指令碼
- python動態載入(三)Python
- tinker熱修復——dex補丁載入過程
- js動態載入實現提高網頁載入速度JS網頁
- EasyUI Jquery 動態載入樹,點選節點載入UIjQuery
- echarts遷移圖動態載入Echarts
- 如何動態載入js檔案JS
- Angularjs動態載入ECharts(一)AngularJSEcharts