zoukankan      html  css  js  c++  java
  • DexClassLoader和PathClassLoader

    Android的ClassLoader体系

     在Android中可以跟java一样实现动态加载jar,但是Android使用Dalvik VM,不能直接加载java打包jar的byte code,需要通过dx工具来优化Dalvik byte code
     Android在API中给出可动态加载的有:DexClassLoader 和 PathClassLoader。

    DexClassLoader:可加载jar、apk和dex,可以从SD卡中加载
    PathClassLoader:只能加载已安装到系统中(即/data/app目录下)的apk文件

    为什么PathClassLoader只能加载apk的文件?

    从上图可以明显知道他们都继承BaseDexClassLoader 在看看他们源码有什么区别

    // DexClassLoader.java
    public class DexClassLoader extends BaseDexClassLoader {
        public DexClassLoader(String dexPath, String optimizedDirectory,
                String libraryPath, ClassLoader parent) {
            super(dexPath, new File(optimizedDirectory), libraryPath, parent);
        }
    }
    
    // PathClassLoader.java
    public class PathClassLoader extends BaseDexClassLoader {
        public PathClassLoader(String dexPath, ClassLoader parent) {
            super(dexPath, null, null, parent);
        }
    
        public PathClassLoader(String dexPath, String libraryPath,
                ClassLoader parent) {
            super(dexPath, null, libraryPath, parent);
        }
    }

    主要的区别在于PathClassLoader的optimizedDirectory参数只能是null,那么optimizedDirectory是做什么用的呢?需要去父类看BaseDexClassLoader

    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
                String libraryPath, ClassLoader parent) {
            super(parent);
            this.originalPath = dexPath;
            this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
        }

    他们的父类构造方法中 是new 一个DexPathList实例把optimizedDirectory参数传入

     public DexPathList(ClassLoader definingContext, String dexPath,
                String libraryPath, File optimizedDirectory) {
            ……
            this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory);
        }
    
        private static Element[] makeDexElements(ArrayList<File> files,
                File optimizedDirectory) {
            ArrayList<Element> elements = new ArrayList<Element>();
            for (File file : files) {
                ZipFile zip = null;
                DexFile dex = null;
                String name = file.getName();
                if (name.endsWith(DEX_SUFFIX)) {
                    dex = loadDexFile(file, optimizedDirectory);
                } else if (name.endsWith(APK_SUFFIX) || name.endsWith(JAR_SUFFIX)
                        || name.endsWith(ZIP_SUFFIX)) {
                    zip = new ZipFile(file);
                }
                ……
                if ((zip != null) || (dex != null)) {
                    elements.add(new Element(file, zip, dex));
                }
            }
            return elements.toArray(new Element[elements.size()]);
        }
    
        private static DexFile loadDexFile(File file, File optimizedDirectory)
                throws IOException {
            if (optimizedDirectory == null) {
                return new DexFile(file);
            } else {
                String optimizedPath = optimizedPathFor(file, optimizedDirectory);
                return DexFile.loadDex(file.getPath(), optimizedPath, 0);
            }
        }
    
        /**
         * Converts a dex/jar file path and an output directory to an
         * output file path for an associated optimized dex file.
         */
        private static String optimizedPathFor(File path,
                File optimizedDirectory) {
            String fileName = path.getName();
            if (!fileName.endsWith(DEX_SUFFIX)) {
                int lastDot = fileName.lastIndexOf(".");
                if (lastDot < 0) {
                    fileName += DEX_SUFFIX;
                } else {
                    StringBuilder sb = new StringBuilder(lastDot + 4);
                    sb.append(fileName, 0, lastDot);
                    sb.append(DEX_SUFFIX);
                    fileName = sb.toString();
                }
            }
            File result = new File(optimizedDirectory, fileName);
            return result.getPath();
        }

     optimizedDirectory是用来缓存我们需要加载的dex文件的,并创建一个DexFile对象,如果它为null,那么会直接使用dex文件原有的路径来创建DexFile 对象。

    optimizedDirectory必须是一个内部存储路径,无论哪种动态加载,加载的可执行文件一定要存放在内部存储。DexClassLoader可以指定自己的optimizedDirectory,所以它可以加载外部的dex,因为这个dex会被复制到内部路径的optimizedDirectory;而PathClassLoader没有optimizedDirectory,所以它只能加载存在系统中已经安装过的apk里面的内部的dex

     怎么缓存经过优化的classes(odex文件)?

     使用Context.getDir(String, int)方法可以创建一个这样的目录,例如:

    File dexOutputDir = context.getDir("dex", 0);

    DexClassLoader

    public DexClassLoader (String dexPath, String dexOutputDir, String libPath, ClassLoader parent)

    dexPath:dex文件路径列表,多个路径使用”:”分隔 
    dexOutputDir:经过优化的dex文件(odex)文件输出目录 
    libPath:动态库路径(将被添加到app动态库搜索路径列表中) 
    parent:这是一个ClassLoader,这个参数的主要作用是保留java中ClassLoader的委托机制(优先父类加载器加载classes,由上而下的加载机制,防止重复加载类字节码)

    PathClassLoader

    PathClassLoader提供两个常用构造方法

    public PathClassLoader (String path, ClassLoader parent)
    
    public PathClassLoader (String path, String libPath, ClassLoader parent)

    path:文件或者目录的列表 
    libPath:包含lib库的目录列表 
    parent:父类加载器

    PathClassLoader提供一个简单的ClassLoader实现,可以操作在本地文件系统的文件列表或目录中的classes,但不可以从网络中加载classes。

    DexClassloader

      编写接口:Dynamic

    package com.smilegames.dynamic.interfaces;
    
    public interface Dynamic {
        public String helloWorld();
        
        public String smileGames();
        
        public String fyt();
    }

     编写实现类:DynamicTest

    package com.smilegames.dynamic.impl;
    
    import com.smilegames.dynamic.interfaces.Dynamic;
    
    public class DynamicTest implements Dynamic {
    
        @Override
        public String helloWorld() {
            return "Hello Word!";
        }
    
        @Override
        public String smileGames() {
            return "Smile Games";
        }
    
        @Override
        public String fyt() {
            return "fengyoutian";
        }
    
    }

    打包并编译成dex
           将接口打包成jar:dynamic.jar(只打包这Dynamic.java这一个接口)
           将实现类打包成jar:dynamic_test.jar(只打包DynamicTest.java这一个实现类)
           将打包后的实现类(dynamic_test.jar)编译成dex:dynamic_impl.jar
                  1、将dynamic_test.jar拷贝到SDK安装目录android-sdk-windowsplatform-tools下(ps:如果platform-tools没有dx.bat,可拷贝到build-tools目录下有dx.bat的子目录)
                  2、执行以下命令:

     dx --dex --output=dynamic_impl.jar dynamic_test.jar

                 3、将dynamic.jar引入测试实例
                 4、将dynamic_impl.jar放到模拟器或真机的sdcard

    修改onCreate例子
           

    @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
    //        或许activity按钮
            helloWorld = (Button) findViewById(R.id.helloWorld);
            smileGames = (Button) findViewById(R.id.smileGames);
            fyt = (Button) findViewById(R.id.fyt);
    
            /*使用DexCkassLoader方式加载类*/
            // dex压缩文件的路径(可以是apk,jar,zip格式)
            String dexPath = Environment.getExternalStorageDirectory().toString() + File.separator + "dynamic_impl.jar";
    
            // dex解压释放后的目录
            //String dexOutputDirs = Environment.getExternalStorageDirectory().toString();
            File dexOutputDir = context.getDir("dex", 0);
            DexClassLoader dexClassLoader = new DexClassLoader(dexPath,dexOutputDir.getAbsolutePath(),null,getClassLoader());
            //主意这里目录必须是应用内的目录
            //如果是应用外的目录例如String dexOutputDirs = Environment.getExternalStorageDirectory().toString()+"XXX";
            //会报:java.lang.IllegalArgumentException: Optimized data directory /storage/sdcard0 is not owned by the current user. Shared storage cannot protect your application from code injection attacks
            //原因之前都已经讲过
    
            // 定义DexClassLoader
            // 第一个参数:是dex压缩文件的路径
            // 第二个参数:是dex解压缩后存放的目录
            // 第三个参数:是C/C++依赖的本地库文件目录,可以为null
            // 第四个参数:是上一级的类加载器
            DexClassLoader dexClassLoader = new DexClassLoader(dexPath,dexOutputDirs,null,getClassLoader());
    
            Class libProvierClazz = null;
            // 使用DexClassLoader加载类
            try {
                libProvierClazz = dexClassLoader.loadClass("com.smilegames.dynamic.impl.DynamicTest");
                // 创建dynamic实例
                dynamic = (Dynamic) libProvierClazz.newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            helloWorld.setOnClickListener(new HelloWorldOnClickListener());
            smileGames.setOnClickListener(new SmileGamesOnClickListener());
            fyt.setOnClickListener(new FytOnClickListener());
        }
    
       private final class HelloWorldOnClickListener implements View.OnClickListener {
            @Override
            public void onClick(View v) {
                if (null != dynamic) {
                    Toast.makeText(getApplicationContext(), dynamic.helloWorld(), 1500).show();
                } else {
                    Toast.makeText(getApplicationContext(), "类加载失败", 1500).show();
                }
            }
        }
    
        private final class SmileGamesOnClickListener implements View.OnClickListener {
            @Override
            public void onClick(View v) {
                if (null != dynamic) {
                    Toast.makeText(getApplicationContext(), dynamic.smileGames(), 1500).show();
                } else {
                    Toast.makeText(getApplicationContext(), "类加载失败", 1500).show();
                }
            }
        }
  • 相关阅读:
    常见试题
    Makefile学习笔记
    安装java
    IntelliJ IDEA for mac 快捷键
    JPA--Caused by: javax.persistence.PersistenceException: [PersistenceUnit: mysqlJPA] Unable to configure EntityManagerFactory
    log4j 配置
    Numbers
    mac 命令
    Intellij IDEA for mac 快捷键
    vim命令
  • 原文地址:https://www.cnblogs.com/mingfeng002/p/7852643.html
Copyright © 2011-2022 走看看