Android动态加载jar/dex
前言
在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优势。本文对网上Android动态加载jar的资料进行梳理和实践在这里与大家一起分享,试图改善频繁升级这一弊病。更多android开源代码请到:www.23code.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
01 |
package com.dynamic; |
02 |
03 |
public interface
IDynamic { |
04 |
public
String helloWorld(); |
05 |
} |
06 |
3.1 . 2 实现类DynamicTest |
07 |
[url=][/url] |
08 |
package com.dynamic; |
09 |
10 |
public class
DynamicTest implements
IDynamic { |
11 |
12 |
@Override |
13 |
public
String helloWorld() { |
14 |
return
"Hello World!" ; |
15 |
} |
16 |
} |
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,如下:
01 |
@Override |
02 |
public
void onCreate(Bundle savedInstanceState) { |
03 |
super .onCreate(savedInstanceState); |
04 |
setContentView(R.layout.main); |
05 |
mToastButton = (Button) findViewById(R.id.toast_button); |
06 |
|
07 |
// Before the secondary dex file can be processed by the DexClassLoader, |
08 |
// it has to be first copied from asset resource to a storage location. |
09 |
// final File dexInternalStoragePath = new File(getDir("dex", Context.MODE_PRIVATE),SECONDARY_DEX_NAME); |
10 |
// if (!dexInternalStoragePath.exists()) { |
11 |
// mProgressDialog = ProgressDialog.show(this, |
12 |
// getResources().getString(R.string.diag_title), |
13 |
// getResources().getString(R.string.diag_message), true, false); |
14 |
// // Perform the file copying in an AsyncTask. |
15 |
// // 从网络下载需要的dex文件 |
16 |
// (new PrepareDexTask()).execute(dexInternalStoragePath); |
17 |
// } else { |
18 |
// mToastButton.setEnabled(true); |
19 |
// } |
20 |
|
21 |
mToastButton.setOnClickListener( new
View.OnClickListener() { |
22 |
public
void onClick(View view) { |
23 |
// Internal storage where the DexClassLoader writes the optimized dex file to. |
24 |
//final File optimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE); |
25 |
final
File optimizedDexOutputPath = new
File(Environment.getExternalStorageDirectory().toString() |
26 |
+ File.separator + "test.jar" ); |
27 |
// Initialize the class loader with the secondary dex file. |
28 |
// DexClassLoader cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(), |
29 |
// optimizedDexOutputPath.getAbsolutePath(), |
30 |
// null, |
31 |
// getClassLoader()); |
32 |
DexClassLoader cl = new
DexClassLoader(optimizedDexOutputPath.getAbsolutePath(), |
33 |
Environment.getExternalStorageDirectory().toString(), null , getClassLoader()); |
34 |
Class libProviderClazz = null ; |
35 |
|
36 |
try
{ |
37 |
// Load the library class from the class loader. |
38 |
// 载入从网络上下载的类 |
39 |
// libProviderClazz = cl.loadClass("com.example.dex.lib.LibraryProvider"); |
40 |
libProviderClazz = cl.loadClass( "com.dynamic.DynamicTest" ); |
41 |
|
42 |
// Cast the return object to the library interface so that the |
43 |
// caller can directly invoke methods in the interface. |
44 |
// Alternatively, the caller can invoke methods through reflection, |
45 |
// which is more verbose and slow. |
46 |
//LibraryInterface lib = (LibraryInterface) libProviderClazz.newInstance(); |
47 |
IDynamic lib = (IDynamic)libProviderClazz.newInstance(); |
48 |
|
49 |
// Display the toast! |
50 |
//lib.showAwesomeToast(view.getContext(), "hello 世界!"); |
51 |
Toast.makeText(MainActivity. this , lib.helloWorld(), Toast.LENGTH_SHORT).show(); |
52 |
} catch
(Exception exception) { |
53 |
// Handle exception gracefully here. |
54 |
exception.printStackTrace(); |
55 |
} |
56 |
} |
57 |
}); |
58 |
} |
3.4 执行结果
这个软件就是按照这个原理实现的,感兴趣的跳转到这里