Android类加载器

Android动态加载jar/dex——DexClassLoader类加载器


前言:

像eclipse、chrome浏览器中的插件,这些插件都是为了在一个主程序中实现比较通用的功能,把用户自定义扩展的功能不附加在主程序中,主程序可在运行时安装和卸载。还有在实际项目中,有些因为业务频繁的升级,造成了较差的用户体验,而使用以插件的形式会改善这类弊端。在Android如何实现插件已经被广泛使用,实现原理都是实现一套插件接口,把插件实现编成apk或者dex,然后在运行时使用DexClassLoader动态加载进来,不过在这个开发过程中会遇到一点问题,这里就介绍一下两种使用DexClassLoader来实现插件的功能。


一、概念

1.1 首先需要了解一点:在Android中可以动态加载,但无法像Java中那样方便动态加载

原因: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 方案一:(编写接口和实现,通过反射来调用插件程序中的方法)
2.1.1 接口Iinterface
package com.putaolab.dex;
   public interface Iinterface 
    {
        public String getData();
    }


2.1.2 实现类Iclass
public class Iclass implements Iinterface {
    public Context context;

    public Iclass(Context context)
{
    this.context = context;
 }
  @Override
 public String getData()
{
    // TODO Auto-generated method stub
    return "hello,i am from the method of getData()...";
 }
}

2.1.3  打包并转成dex

    > **选中工程,常规流程导出即可(一定注意:打包请不要把接口文件打进来,我就是因为把接口文件打包进了,出现了这个异常!**
    > **java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation**)
    > **将打包好的jar拷贝到SDK安装目录android-sdk-windows\platform-tools下(注:这个目录没有的话,在build-tools目录会发现dex.bat批处理指令),DOS进入这个目录,执行命名:
            dx --dex --output=testdex.jar test.jar
2.1.4  调用例子(反射调用-->本文我把jar优化后的testdex.jar从assets目录下复制到了SD卡下来反射加载的;  接口插件--->把testdex.jar从assets目录下复制到了data/data/包名/files目录下来处理的)
 /*法一:把优化后的jar(testdex.jar)复制到SD卡下,通过反射来调用插件的方法*/
    FileUtil.copyAssetJarToFile(this, "testdex.jar", "testdex.jar");
    File file = new File(Environment.getExternalStorageDirectory().toString() + File.separator + "testdex.jar");
    File optimizedDexOutputPath = getDir("temp", Context.MODE_PRIVATE);
    /**
     * DexClassLoader类加载器构造的四个参数
     * String dexPath:the list of jar/apk files containing classes and resources, delimited by File.pathSeparator, which defaults to ":" on Android
     *                  需要装在的apk或者jar文件的路径,包含多个路径用File.pathSeparator间隔开,在Android上默认是“:” 
     * String optimizedDirectory:directory where optimized dex files should be written; must not be null
     *                  优化后的dex文件存放目录,不能为null
     * String libraryPath:the list of directories containing native libraries, delimited by File.pathSeparator; may be null
     *                  目标类中使用的c/c++库的列表,每个目录用File.pathSeparator间隔开,可以为null
     * ClassLoader parent:  the parent class loader
     *                  该类装载器的父装载器,一般用当前执行类的装载器
     */
    DexClassLoader dexClassLoader = new DexClassLoader(file.getAbsolutePath(),
            optimizedDexOutputPath.getAbsolutePath(),
            null,
            getClassLoader());

    try {
        Class<?> loadClass = dexClassLoader.loadClass("com.putaolab.dex.Iclass");
        Constructor<?> constructor = loadClass.getConstructor(new Class[]{Context.class});
        /*法一:反射调用方法*/
        Method method = loadClass.getMethod("getData", null);
        String data = (String) method.invoke(constructor.newInstance(this), null);
        Log.e(tag, "data---"+data);
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
2.2 方案二:(把接口作为联系主程序和插件程序的桥梁,要实现往程序通过接口调用插件的程序,那么主程序和插件程序必须有相同接口的文件,也就两个程序里都有接口的java类文件)
        /*法二:把优化后的jar(testdex.jar)复制到data/data/包名/files/目录下,通过接口来*/
        FileUtil.copyAssetJarToData(this, "testdex.jar", "testdex.jar");
        File file = new File(this.getFilesDir().getAbsolutePath() + File.separator + "testdex.jar");
        File optimizedDexOutputPath = getDir("temp", Context.MODE_PRIVATE);
        DexClassLoader dexClassLoader = new DexClassLoader(file.getAbsolutePath(),
                optimizedDexOutputPath.getAbsolutePath(),
                null,
                getClassLoader());

        try {
            Class<?> loadClass = dexClassLoader.loadClass("com.putaolab.dex.Iclass");
            Constructor<?> constructor = loadClass.getConstructor(new Class[]{Context.class});
            /*法二:*/
            Iinterface newInstance = (Iinterface) constructor.newInstance(this);
            String data = newInstance.getData();
            Log.e(tag, "data---"+data);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

源代码下载地址:本文相关代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值