Android动态加载jar/dex
来源:互联网 发布:有个网络歌手老头唱歌 编辑:程序博客网 时间:2024/05/17 20:22
前言
在目前的软硬件环境下,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
public interface IDynamic {
public String helloWorld();
}
3.1.2 实现类DynamicTest
public class DynamicTestimplements 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进入这个目录,执行命名:
3.3 修改调用例子
修改MainActivity,如下:
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),如果如下命令:
6.2 2012-3-29 本文升级版:
Android应用开发提高系列(4)——Android动态加载(上)——加载未安装APK中的类
请大家参照最新的文章来做动态加载!
结束
除了翻译组的工作和自己本职的工作以外,很难抽时间出来分享一些开发心得,但正所谓挤挤总是有的,欢迎交流!
PS:我照着做了,但是有个问题。
如果我android 工程中有DynamicTest.java那么不把dynamic.dex拷到手机上,竟然不会有问题。
如果工程中删除了DynamicTest.java,然后把dynamic.dex文件拷到sd卡根目录,就会找不到类。
这两种情况下,接口IDynamic都是有的。
请问如何解决?
-----
问题解决了:
打包成jar,而不是dex文件,然后调用jar包,并且jar包中去掉接口。
新问题是:加载有些慢
还有我看到有些项目是直接可以加载别的apk的内容 这个怎么做额?
怎样打包和加载资源文件,比如我要现实一个activity,是不是只能打包成APK了。
没有调用APK的经验,有时间可以研究一下,调用其他APK里的资源可能会有一个问题,资源ID与当前APK资源ID有可能相同。
我做了一个小例子,一个android工程,我导出jar文件。而且用dx工具处理成带classes.dex文件和资源文件等的jar包。
用同样的方法加载activity类,却提示找不到类,而且jar包也没有解压。
如果单独处理一个java文件,则没有错误,jar包也会解压。
求教伯伯~
注意:在实践中发现,自己新建一个Java工程然后导出jar是无法使用的,这一点大家可以根据文章一来了解相关原因,也是本文的重点之一。这里打包导出为dynamic.jar
请仔细看文章,看看是不是这个原因。
请问可以分享下只打成jar包,不打dex包的方式吗?
helloworld这个字符串主程序里面是没有的(打包完可直接把DynamicTest删除)。
有截图如何打包。
试过,如果把DynamicTest删除,则class not find
按照上述方法打的dex不行,用你提供下载的dex也一样。
ps:2楼的问题应该跟我是一样的。
感谢反馈,我这边再检查一下看看哪里出了问题,你看看这篇文章:
http://yunfeng.sinaapp.com/?p=87
本文基本上是参照这篇文章来做的。有结果了会更新文章,再次感谢!
问题确实存在,感谢反馈,已经在文章顶部醒目位置提醒后来阅读者,我在仔细研究一下,欢迎交流,再次感谢!
@liuzhaocn
感谢二位的反馈,文章已经做了修复,期待两位有更多发现并分享 :)
请问农伯有没做过动态加载.so,比如sdcard上的.so文件
so本身使用方式就是动态加载,不过没做过在sdcard上的。
java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation错误,貌似加载类时出现了两个jar,请问知道这是什么原因吗,无比感谢!
IllegalAccessError 违法访问错误。当一个应用试图访问、修改某个类的域(Field)或者调用其方法,但是又违反域或方法的可见性声明,则抛出该异常
你可以搜一下ant相关的资料。
谢谢回复了,ant我知道,但是当我命令行去执行dx的命令时候老是出现不匹配异常,蛋疼
正准备写一篇新的动态加载的文章,其实不用这么麻烦,直接新建一个android项目,打包成apk,直接把这里调用jar换成apk即可。
既然能动态加载apk,那么能不能调用已安装程序的类和方法?
更加可以,主要是ClassLoader,已安装的能获得Context,动态的无法获取这个。
哦,谢谢了,但是我用接口接收的时候 java.lang.ClassCastException就出来了,具体用什么来接收呢?
是用context.getClassLoader()获取ClassLoader然后loader的吗?
哦,知道了,可以通过getMethod直接使用类里面的方法,呵呵,谢谢伯伯!!!
这篇文章写的很好, 有个问题也想请教一下, 这个dex里面可不可以再访问其他的JAR包啊,是否支持这种形式呢?
你的工程包中com.dynamic.DynamicTest 在主工程中删除试验下,并且这个类不可以写在主工程中,当删除后按照你目前提供的dex,还是会找不到DynamicTest类的,我做了apk的动态调用方式,接口在上层,实现在apk中,没有问题,你看下这个dex或者jar如何打包可以实现正常访问。
请参考本文 后期维护说明。
只是提醒后来者,参照范例的时候注意,范例不正确
如果不删除本例中DynamicTest 类,则通过反射得到的类并非从导出的jar包中加载的,而是从当前运行的程序中加载到的
楼主可以调试你的例子,DynamicTest 是从PathClassLoader中加载而非从DexClassLoader中加载,也就说类加载在父classloader中已经加载,跟你导出的dex文件没关系
怎么动态加载另外一个android项目(已经是apk了)?
有没有什么办法可以不强退?
强退,有logcat输出么?
有些tag是debug的信息,都是一些汇编方面的信息,但是信息太多了就不再这里贴出了.
你有邮箱吗?
我发你邮箱里,你帮我看下是什么问题,谢谢了!!!!
另外:你的例子真的有点问题,就是30楼说的问题.
注意看 文章末尾的后期维护部分。
你好!感谢你的文章,对我的帮助很大。初学另请教个问题:请问如果想从本工程跳转到jar包中的activity运行要如何操作?
r dynamic.jar
'dx' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
这个是为什么呢?
你看一下platform-tools是不是有dx,是不是后续版本移到其他文件夹去了
用dex生成classes.dex文件时,把libs下的jar包路径注掉
<dex
dexedlibs="
executable="
nolocals="@{nolocals}"
output="
<path path="${out.dex.input.absolute.dir}" />
<!--<path refid="out.dex.jar.input.ref" />-->
<external-libs />
</dex>
我按照这个例子试了一下,当我把DynamicTest.java删除后,会提示DynamicTest类找不到。我打包时没有将IDynamic.java打进来,而且也用dx工具处理过了。我测试用的手机系统是Android4.3,请问这个和系统有关吗?
我按照这个例子试了一下,当我把DynamicTest.java删除后,会提示找不到DynamicTest类。我打包时没有将IDynamic.java打进来,而且也用dx工具处理过了。我测试用的手机系统是Android4.3,请问这个和系统有关吗?
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Android动态加载jar/dex
- Model1Model2设计模式
- C#篇之---XML
- 如何学好C++语言
- 面试基础_01字符串反转操作
- 段间跳转指令jmpi和实模式寻址
- Android动态加载jar/dex
- 简单表单框
- javaweb工程获取webContent下WEB-INF下的配置文件
- Java GUI之文本区JTextArea
- 推荐漂亮的flash网页MP3音乐播放器
- Python 正则表达式验证Combined Log
- 关于TP-LINK宽带路由器上的“转发规则”功能用途及设置办法 -- LAN与WAN通信的IP转换
- E9 搭建 NFS 环境并从 NFS 启动开发板
- arcengine对featureclass重命名问题