zoukankan      html  css  js  c++  java
  • Android中的类装载器DexClassLoader

    http://blog.csdn.net/com360/article/details/14125683

    java中,有个概念叫做“类加载器”(ClassLoader),它的作用就是动态的装载Class文件。标准的java sdk中有一个
    ClassLoader类,借助这个类可以装载想要的Class文件,每个ClassLoader对象在初始化时必须制定Class文件的路径。
        可能有人会问,在写程序的时候不是有import关键字可以引用制定的类吗?为何还要使用这个类加载器呢?
        原因其实是这样的,使用import关键字引用的类必须符合以下两个条件
    
           类文件必须在本地,当程序运行时需要次类时,这时类装载器会自动装载该类,程序员不需要关注此过程。
           编译的时候必须有这个类文件,否则编译不通过。
    
        如果想让程序在运行的时候动态调用怎么办呢?用import显示是不符合上面的两种要求的。此时ClassLoader就派上用场了。
        关于java的Classloader的装载机制请参考此链接 http://www.iteye.com/topic/83978,最好到网上自行搜索一下。
        http://www.artima.com/insidejvm/ed2/《Inside the Java Virtural Machine》
        对于android应用程序,本质上使用的是java开发,使用标准的java编译器编译出Class文件,和普通的java开发不同的
        地方是把class文件再重新打包成dex类型的文件,这种重新打包会对Class文件内部的各种函数表、变量表等进行优化,
        最终产生了dex文件。dex文件是一种经过android打包工具优化后的Class文件,因此加载这样特殊的Class文件就需要特殊的类装载器,
        所以android中提供了DexClassLoader类。
    类装载器DexClassLoader类结构
    继承关系:
        
    
    A class loader that loads classes from .jar and .apk files containing a classes.dex entry. This can be used to execute code not installed as part of an application.
    
    This class loader requires an application-private, writable directory to cache optimized classes. Use Context.getDir(String, int) to create such a directory:
    
       File dexOutputDir = context.getDir("dex", 0);
     
    
    Do not cache optimized classes on external storage. External storage does not provide access controls necessary to protect your application from code injection attacks.
    翻译:这个类加载器用来从.jar和.apk类型的文件内部加载classes.dex文件。通过这种方式可以用来执行非安装的程序代码,作为程序的一部分进行运行。
        这个装载类需要一个程序私有的,可写的文件目录去存放优化后的classes文件。通过Contexct.getDir(String, int)来创建
    
    这个目录:
    
         File dexOutputDir = context.getDir("dex", 0);
    
    不要把优化优化后的classes文件存放到外部存储设备上,防代码注入攻击。
    
    
    这个类只有一个构造函数:
    public DexClassLoader (String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)
    Added in API level 3
    
    Creates a DexClassLoader that finds interpreted and native code. Interpreted classes are found in a set of DEX files contained in Jar or APK files.
    
    创建一个DexClassLoader用来找出指定的类和本地代码(c/c++代码)。用来解释执行在DEX文件中的class文件。
    
    路径的分隔符使用通过System的属性 path.separator 获得 :.
    
    String separeater = System.getProperty("path.separtor");
    Parameters
    dexPath     需要装载的APK或者Jar文件的路径。包含多个路径用File.pathSeparator间隔开,在Android上默认是 ":" 
    optimizedDirectory     优化后的dex文件存放目录,不能为null
    libraryPath     目标类中使用的C/C++库的列表,每个目录用File.pathSeparator间隔开; 可以为 null
    parent     该类装载器的父装载器,一般用当前执行类的装载器
    类装载器DexClassLoader的具体使用
    
    这个类的使用过程基本是这样:
    
        通过PacageMangager获得指定的apk的安装的目录,dex的解压缩目录,c/c++库的目录
        创建一个 DexClassLoader实例
        加载指定的类返回一个Class
        然后使用反射调用这个Class
    
    下面是代码部分,代码参考自《Android内核剖析》(作者柯元旦,这本书不错,推荐阅读):
    [html] view plaincopy
    
        @SuppressLint("NewApi") private void useDexClassLoader(){  
            //创建一个意图,用来找到指定的apk  
            Intent intent = new Intent("com.suchangli.android.plugin", null);  
            //获得包管理器  
            PackageManager pm = getPackageManager();  
            List<ResolveInfo> resolveinfoes =  pm.queryIntentActivities(intent, 0);  
            //获得指定的activity的信息  
            ActivityInfo actInfo = resolveinfoes.get(0).activityInfo;  
              
            //获得包名  
            String pacageName = actInfo.packageName;  
            //获得apk的目录或者jar的目录  
            String apkPath = actInfo.applicationInfo.sourceDir;  
            //dex解压后的目录,注意,这个用宿主程序的目录,android中只允许程序读取写自己  
            //目录下的文件  
            String dexOutputDir = getApplicationInfo().dataDir;  
              
            //native代码的目录  
            String libPath = actInfo.applicationInfo.nativeLibraryDir;  
            //创建类加载器,把dex加载到虚拟机中  
            DexClassLoader calssLoader = new DexClassLoader(apkPath, dexOutputDir, libPath,  
                    this.getClass().getClassLoader());  
              
            //利用反射调用插件包内的类的方法  
              
            try {  
                Class<?> clazz = calssLoader.loadClass(pacageName+".Plugin1");  
                  
                Object obj = clazz.newInstance();  
                Class[] param = new Class[2];  
                param[0] = Integer.TYPE;  
                param[1] = Integer.TYPE;  
                  
                Method method = clazz.getMethod("function1", param);  
                  
                Integer ret = (Integer)method.invoke(obj, 1,12);  
                  
                Log.i("Host", "return result is " + ret);  
                  
            } catch (ClassNotFoundException e) {  
                e.printStackTrace();  
            } catch (InstantiationException e) {  
                e.printStackTrace();  
            } catch (IllegalAccessException e) {  
                e.printStackTrace();  
            } catch (NoSuchMethodException e) {  
                e.printStackTrace();  
            } catch (IllegalArgumentException e) {  
                e.printStackTrace();  
            } catch (InvocationTargetException e) {  
                e.printStackTrace();  
            }  
           }  
    
    Plugin1.apk中的一个类:
    [html] view plaincopy
    
        package com.suchangli.plugin1;  
          
        public class Plugin1 {  
            public int function1(int a, int b){  
                  
                return a+b;  
            }  
        }  
    
    
    
    Log输出的结果:
    注意要在Plugin1中的清单文件的一个activity中添加一个如下的action,这个action必须是宿主和插件约定好的。
    源代码:
    通过以上代码就可以访问另一个apk中的代码了,一个比较麻烦的地方就是必须用反射才能调用方法,
    有没不用反射也可以调用方法方式呢?当然有,通过接口。
  • 相关阅读:
    android -------- Data Binding的使用(二)
    牛客网-《剑指offer》-数值的整数次方[快速幂运算]
    牛客网-《剑指offer》-二进制中1的个数
    牛客网-《剑指offer》-矩形覆盖
    牛客网-《剑指offer》-变态跳台阶
    牛客网-《剑指offer》-跳台阶
    牛客网-《剑指offer》-斐波那契数列
    牛客网-《剑指offer》-旋转数组的最小数
    牛客网-《剑指offer》-用两个栈实现队列
    牛客网-《剑指offer》-重建二叉树
  • 原文地址:https://www.cnblogs.com/zhengtu2015/p/5242940.html
Copyright © 2011-2022 走看看