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中的类
请你们参照最新的文章来作动态加载!
结束
除了翻译组的工做和本身本职的工做之外,很难抽时间出来分享一些开发心得,但正所谓挤挤老是有的,欢迎交流!