本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《 阿里云开发者社区用户服务协议 》和 《 阿里云开发者社区知识产权保护指引 》。如果您发现本社区中有涉嫌抄袭的内容,填写 侵权投诉表单 进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

一、 基本概念和注意点

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  执行结果

四、参考文章

[推荐]在Android中动态载入自定义类

Android app中加载jar插件

关于Android的ClassLoader探索

Android App 如何动态加载类

大家可以看看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