Android类加载器

来源:互联网 发布:大学英语单词听力软件 编辑:程序博客网 时间:2024/05/27 02:31

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 接口Iinterfacepackage com.putaolab.dex;   public interface Iinterface     {        public String getData();    }2.1.2 实现类Iclasspublic 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.jar2.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();        }

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

1 0
原创粉丝点击