zoukankan      html  css  js  c++  java
  • 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 package com.dynamic;
    02  
    03 public interface IDynamic {
    04     public String helloWorld();
    05 }
    06       3.1.2  实现类DynamicTest
    07 [url=][/url]
    08 package com.dynamic;
    09  
    10 public class DynamicTest implements IDynamic {
    11  
    12     @Override
    13     public String helloWorld() {
    14         return "Hello World!";
    15     }
    16 }




        3.2  打包并转成dex

          3.2.1  选中工程,常规流程导出即可,如图:

          注意:在实践中发现,自己新建一个Java工程然后导出jar是无法使用的,这一点大家可以根据文章一来了解相关原因,也是本文的重点之一。这里打包导出为dynamic.jar

          (后期修复:打包请不要把接口文件打进来,参见文章末尾后续维护!)

          3.2.2  将打包好的jar拷贝到SDK安装目录android-sdk-windowsplatform-tools下,DOS进入这个目录,执行命名:

    dx --dex --output=test.jar dynamic.jar

        3.3  修改调用例子

          修改MainActivity,如下:

    01 @Override
    02     public void onCreate(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(new View.OnClickListener() {
    22             public void onClick(View view) {
    23                 // Internal storage where the DexClassLoader writes the optimized dex file to.
    24                 //final File optimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE);
    25                 final File optimizedDexOutputPath = new File(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 = new DexClassLoader(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  执行结果

        


    这个软件就是按照这个原理实现的,感兴趣的跳转到这里

  • 相关阅读:
    单例模式
    spring boot 调度任务
    linux yum安装MySQL5.6
    day5模块学习--sys模块
    day5模块学习 -- os模块学习
    day5模块学习--random模块
    day5模块学习 -- time、datetime时间模块
    生成随机验证码的方法
    SQL中format()函数对应的格式
    day4作业小代码练习
  • 原文地址:https://www.cnblogs.com/wanqieddy/p/3715501.html
Copyright © 2011-2022 走看看