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 | packagecom.dynamic; | 
| 02 | 
| 03 | publicinterfaceIDynamic { | 
| 04 |     publicString helloWorld(); | 
| 05 | } | 
| 06 |       3.1.2  实现类DynamicTest | 
| 07 | [url=][/url] | 
| 08 | packagecom.dynamic; | 
| 09 | 
| 10 | publicclassDynamicTest implementsIDynamic { | 
| 11 | 
| 12 |     @Override | 
| 13 |     publicString 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 |     publicvoidonCreate(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(newView.OnClickListener() { | 
| 22 |             publicvoidonClick(View view) { | 
| 23 |                 // Internal storage where the DexClassLoader writes the optimized dex file to. | 
| 24 |                 //final File optimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE); | 
| 25 |                 finalFile optimizedDexOutputPath = newFile(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 = newDexClassLoader(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 执行结果
    
这个软件就是按照这个原理实现的,感兴趣的跳转到这里
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号