Android動態加載jar/dex
前言 html
在目前的軟硬件環境下,Native App與Web App在用戶體驗上有着明顯的優點,但在實際項目中有些會由於業務的頻繁變動而頻繁的升級客戶端,形成較差的用戶體驗,而這也偏偏是Web App的優點。本文對網上Android動態加載jar的資料進行梳理和實踐在這裏與你們一塊兒分享,試圖改善頻繁升級這一弊病。 java
正文 app
1、 基本概念和注意點 ide
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、 準備
本文主要參考"4、參考文章"中第一篇文章,補充細節和實踐過程。
2.1 下載開源項目
http://code.google.com/p/goodev-demo
將項目導入工程,工程報錯的話應該是少了gen文件夾,手動添加便可。注意這個例子是從網上下載優化好的jar(已經優化成dex而後再打包成的jar)到本地文件系統,而後再從本地文件系統加載並調用的。本文則直接改爲從SD卡加載。
3、實踐
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 執行結果
4、參考文章
[推薦]在Android中動態載入自定義類
Android app中加載jar插件
關於Android的ClassLoader探索
Android App 如何動態加載類
5、補充
你們能夠看看DexClassLoader的API文檔,裏面不提倡從SD卡加載,不安全。此外,我也正在組織翻譯組儘快把這個命名空間下的幾個類都翻譯出來,以供你們參考。
工程下載:這裏,Dex文件下載:這裏。你們能夠直接把Dex文件拷貝到SD卡,而後運行例子。
6、後期維護
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中的類
請你們參照最新的文章來作動態加載!
結束
除了翻譯組的工做和本身本職的工做之外,很難抽時間出來分享一些開發心得,但正所謂擠擠老是有的,歡迎交流!