• 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中的代码了,一个比较麻烦的地方就是必须用反射才能调用方法,
    有没不用反射也可以调用方法方式呢?当然有,通过接口。
  • 相关阅读:
    中秋假期乱做
    记一道典题 分层图+同余系建图
    Java 创建对象的几种方式
    1029 Median (25 分)(two pointers)
    1107 Social Clusters (30 分)(并查集)
    1153 Decode Registration Card of PAT (25 分)(模拟,排序,map)
    1143 Lowest Common Ancestor (30 分)(二叉查找树)
    1149 Dangerous Goods Packaging (25 分)
    1152 Google Recruitment (20 分)(字符串处理)
    1154 Vertex Coloring (25 分)(set,hash)
  • 原文地址:https://www.cnblogs.com/zhengtu2015/p/5242940.html
Copyright © 2020-2023  润新知