前言

   在目前的软硬件环境下,Native App与Web App在用户体验上有着明显的优势,但在实际项目中有些会因为业务的频繁变更而频繁的升级客户端,造成较差的用户体验,而这也恰恰是Web App的优势。本文对网上Android动态加载jar的资料进行梳理和实践在这里与大家一起分享,试图改善频繁升级这一弊病。

声明

  欢迎转载,但请保留文章原始出处:)

    51CTO:http://over140.blog.51cto.com

农民伯伯: http://over140.cnblogs.com

    Android中文翻译组: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

packagecom.dynamic;

public interfaceIDynamic{
publicStringhelloWorld();
}

      3.1.2  实现类DynamicTest

packagecom.dynamic;

public classDynamicTest implementsIDynamic{

@Override
publicStringhelloWorld(){
return"HelloWorld!";
}
}

    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 voidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mToastButton=(Button)findViewById(R.id.toast_button);

// BeforethesecondarydexfilecanbeprocessedbytheDexClassLoader,
// ithastobefirstcopiedfromassetresourcetoastoragelocation.
// finalFiledexInternalStoragePath=newFile(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);
// // PerformthefilecopyinginanAsyncTask.
// // 从网络下载需要的dex文件
// (newPrepareDexTask()).execute(dexInternalStoragePath);
// }else{
// mToastButton.setEnabled(true);
// }

mToastButton.setOnClickListener( newView.OnClickListener(){
public voidonClick(Viewview){
// InternalstoragewheretheDexClassLoaderwritestheoptimizeddexfileto.
// finalFileoptimizedDexOutputPath=getDir("outdex",Context.MODE_PRIVATE);
finalFileoptimizedDexOutputPath= newFile(Environment.getExternalStorageDirectory().toString()
+File.separator+"test.jar");
// Initializetheclassloaderwiththesecondarydexfile.
// DexClassLoadercl=newDexClassLoader(dexInternalStoragePath.getAbsolutePath(),
// optimizedDexOutputPath.getAbsolutePath(),
// null,
// getClassLoader());
DexClassLoadercl= newDexClassLoader(optimizedDexOutputPath.getAbsolutePath(),
Environment.getExternalStorageDirectory().toString(), null,getClassLoader());
ClasslibProviderClazz= null;

try{
// Loadthelibraryclassfromtheclassloader.
// 载入从网络上下载的类
// libProviderClazz=cl.loadClass("com.example.dex.lib.LibraryProvider");
libProviderClazz=cl.loadClass("com.dynamic.DynamicTest");

// Castthereturnobjecttothelibraryinterfacesothatthe
// callercandirectlyinvokemethodsintheinterface.
// Alternatively,thecallercaninvokemethodsthroughreflection,
// whichismoreverboseandslow.
// LibraryInterfacelib=(LibraryInterface)libProviderClazz.newInstance();
IDynamiclib=(IDynamic)libProviderClazz.newInstance();

// Displaythetoast!
// lib.showAwesomeToast(view.getContext(),"hello世界!");
Toast.makeText(MainActivity. this,lib.helloWorld(),Toast.LENGTH_SHORT).show();
} catch(Exceptionexception){
// Handleexceptiongracefullyhere.
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.jardynamic.jar

结束

  除了翻译组的工作和自己本职的工作以外,很难抽时间出来分享一些开发心得,但正所谓挤挤总是有的,欢迎交流!

更多相关文章

  1. Android中WebView加载本地Html,与JavaScript与Android方法相互传
  2. Android Studio 文件类型图标
  3. Android的NDK开发(5)————Android JNI层实现文件的read、writ
  4. Android动态加载及hook资料汇总
  5. Android之ViewFlipper的使用与ListView的分页加载
  6. Android studio 3.1.3 无法获取pom文件
  7. Android Settings 设置中 PreferenceActivity中,Header是如何被加

随机推荐

  1. Android高手进阶教程(十七)之---Android
  2. Android深入浅出之 Surface
  3. Android开机自动运行服务
  4. Android实现图表绘制和展示
  5. 关于Zipalign的介绍和使用方法
  6. 在 Ubuntu 10.04 下面安装 Android(安卓)
  7. Android之——获取进程总数、内存、任务
  8. 以一个小程序设计来入门Android
  9. Android软硬整合技术(HAL&Framework)
  10. 一篇文章带你了解 Android(安卓)消息机制