美文网首页简书IT安卓相关进阶android学习
Android应用程序插件化研究之DexClassLoader

Android应用程序插件化研究之DexClassLoader

作者: 大利猫 | 来源:发表于2016-02-28 17:28 被阅读5322次

    文章首发:Android应用程序插件化研究之DexClassLoader|大利猫

    最近在研究Android应用的插件化开发,看了好几个相关的开源项目,插件化都是在解决以下几个问题:

    • 如何把插件apk中的代码和资源加载到当前虚拟机。
    • 如何把插件apk中的四大组件注册到进程中。
    • 如何防止插件apk中的资源和宿主apk中的资源引用冲突。

    围绕着这几个问题,我将会写系列文章逐步分析插件化的原理和实现方案。本篇文章主要讲第一点:如何加载另一个apk中的class。我们要把一个包含class文件的jar加载到java虚拟机中,需要使用ClassLoader这个类。Android的编译系统中对class文件进行了进一步的处理:最后变成 .dex文件,.dex文件包含在apk中,google提供了一个类来加载.dex文件,这个类就是DexClassLoader,它继承自ClassLoader。本篇的重点是写一个例子来说明DexClassLoader的用法。先来熟悉下DexClassLoader。

    DexClassLoader介绍

    DexClassLoader是一个类加载器,可以用来从.jar和.apk文件中加载class。可以用来加载执行没用和应用程序一起安装的那部分代码。构造函数:DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)

    dexPath:被解压的apk路径,不能为空。

    optimizedDirectory:解压后的.dex文件的存储路径,不能为空。这个路径强烈建议使用应用程序的私有路径,不要放到sdcard上,否则代码容易被注入攻击。

    libraryPath:os库的存放路径,可以为空,若有os库,必须填写。

    parent:父亲加载器,一般为context.getClassLoader(),使用当前上下文的类加载器。
    关于DexClassLoader更深入的理解,我建议读者先去研究一下Java的累加载器,然后在去读一读DexClassLoader的源码,本章不做深究,接下来写一个DemoDex来演示ClassLoader的使用。

    创建一个插件apk工程module:bundle

    我把插件的包名命名为:com.dexclassdemo.liuguangli.apkbeloaded,我们创建一个类:ClassToBeLoad:

    package com.dexclassdemo.liuguangli.apkbeloaded;
      import android.util.Log;
      public class ClassToBeLoad {    
      public  void method(){      
            Log.v("ClassToBeLoad", "called method of class " +       ClassToBeLoad.class.getName());  
      }
    }
    

    我们会演示这个方法如何在宿主中被调用的,并且我们可以跟踪这个类的类加载器。我们编译这个工程得到的ask文件为:apkbeloaded-debug.apk。

    创建一个宿主apk工程

    我把宿主包名命名为:dexclassloaderdemo。我们在assets目录下创建一个目录plugins,然后把apkbeloaded-debug.apk拷贝到该目录下。在MainActivity中创建一个方法为:loadApk

    public  void loadApk(String apkPath) {   
     Log.v("loadDexClasses", "Dex Preparing to loadDexClasses!");   
     File dexOpt = this.getDir("dexOpt", MODE_PRIVATE);       
     final DexClassLoader classloader = new DexClassLoader(                    apkPath,               
     dexOpt.getAbsolutePath(),      
          null,               
     this.getClassLoader());     
       Log.v("loadDexClasses", "Searching for class : "                + "com.registry.Registry");       
     try {        
        Class<?> classToLoad = (Class<?>)     classloader.loadClass("com.dexclassdemo.liuguangli.apkbeloaded.ClassToBeLoad");          
      Object instance = classToLoad.newInstance();         
       Method method = classToLoad.getMethod("method");    
            method.invoke(instance);   
     } catch (IllegalAccessException e) { 
           e.printStackTrace();     
       } catch (ClassNotFoundException e) {   
         e.printStackTrace();      
      } catch (NoSuchMethodException e) {     
         e.printStackTrace();      
      } catch (InvocationTargetException e) {    
        e.printStackTrace();     
       } catch (InstantiationException e) {         
        e.printStackTrace();       
     }
    }
    

    我们从apkPath读取文件加载(本例中拷贝到sdcard,实际开发中最好拷贝到私有目录,防止被注入)。

    最后我们来再 onCreate 方法中调用 loadApk :

    @Override
    protected void onCreate(Bundle savedInstanceState) {       
     super.onCreate(savedInstanceState);    
     setContentView(R.layout.activity_main);  
      String apkPath = Environment.getExternalStorageDirectory() + "/bundle.apk";    loadApk(apkPath);
    }  
    

    编译 bundle 工程,然后把边缘后的 apk 文件拷贝到测试机的 sdcard 目录下,更名为 bundle.apk。 然后运行宿主 apk 验证结果。
    Demo源码
    下载源码:https://github.com/liuguangli/DexClassLoaderDemo
    下篇文章研究:如何加载插件中的资源

    相关文章

      网友评论

      • 渡过:1.“我们在assets目录下创建一个目录plugins,然后把apkbeloaded-debug.apk拷贝到该目录下。”这个操作有什么目的?
        2.Github仓库Demo跑还是找不到类
      • 宇智波ALLen:在android 23以下的时候,在manifest中是可以直接添加权限,23及其以上就不行了,需要动态申请
        宇智波ALLen:或者你直接把targetSdkVersion 改为22
      • b85958901227:大神,有研究过宿主app和插件apk中含有相同jar包如何处理么?
        b85958901227:Class resolved by unexpected DEX: Lcom/gst/dltest/ActTest;(0x41441030):0x5d97c000 ref [Lcom/gst/mydllib/pluginJar/DLBaseActivity;] Lcom/gst/mydllib/pluginJar/DLBaseActivity;(0x413e63d0):0x5d5c7000
        (Lcom/gst/dltest/ActTest; had used a different Lcom/gst/mydllib/pluginJar/DLBaseActivity; during pre-verification)
        Unable to resolve superclass of Lcom/gst/dltest/ActTest; (2150)
      • 我在等你回复可你没回: W System.err: java.lang.ClassNotFoundException: Didn't find class "com.dexclassdemo.liuguangli.apkbeloaded.ClassToBeLoad" on path: DexPathList[[zip file "/storage/emulated/0/bundle.apk"],nativeLibraryDirectories=[/vendor/lib64, /system/lib64]]

        楼主不会找不到类吗
        大利猫:@九九叔 谢谢提醒
        我在等你回复可你没回:@九九叔 建议楼主完善下,获取下运行时权限
        我在等你回复可你没回:知道了,原来是sd卡权限被系统限制了。
        http://blog.csdn.net/autumn_xl/article/details/53262856
      • 5ce91cc66c8b:github仓库请把permission声明放到application的外侧
      • HuDP:学习
      • HuDP:libraryPath是so库的存放路径嘛?
        大利猫:@HuDP
        String: the list of directories containing native libraries, delimited by File.pathSeparator; may be null
        这是官方的解释,你可以研究下,有新的发现告诉我哈

      本文标题:Android应用程序插件化研究之DexClassLoader

      本文链接:https://www.haomeiwen.com/subject/clfgkttx.html