Android动态加载jar/dex

Android动态加载jar/dex


前言 html

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

 

声明 android

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

    博客园:http://www.cnblogs.com 安全

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

    Android中文翻译组:http://androidbox.sinaapp.com/ 网络

 

正文 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中的类

      请你们参照最新的文章来作动态加载!

 

结束

  除了翻译组的工做和本身本职的工做之外,很难抽时间出来分享一些开发心得,但正所谓挤挤老是有的,欢迎交流!
相关文章
相关标签/搜索