Administrator
发布于 2023-12-11 / 19 阅读
0
0

dex生成和加载

生成dex文件

step1:将java文件编译成class文件

命令行方式:

javac YourJavaFile.java

编译后文件夹查找

android下build后,YouProject\Module\build\intermediates\javac\debug\classes

step2: 将class 文件打包成jar

说明, “./” 是当前目录 ,直接 “.” 也可以

D:\input>jar cvf 1.jar ./

step3:将jar文件 转 dex文件

D:\Android_Sdk\build-tools\34.0.0>d8 --release --output classes.zip d:/input/1.jar

classes.zip 里面,就是dex文件

加载dex文件

示例代码

方式1:PathClassLoader

private void loadClassPathClassLoader() { //>android 8
        try {
			//app 内部的
            Context context = getApplicationContext();
            String dexDirectoryName = "dex"; // 存放DEX文件的子目录名称
            File dexDirectory = context.getDir(dexDirectoryName, Context.MODE_PRIVATE);
            String dexPath = new File(dexDirectory, "1.dex").getAbsolutePath(); // 替换为实际的DEX文件路径

            //外部存储环境也可以实现
//            dexPath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/1.dex";



            ClassLoader parentClassLoader = getClassLoader(); // 父类加载器
            PathClassLoader pathClassLoader = new PathClassLoader(dexPath, parentClassLoader);


            // 使用 dexPath 来加载和处理DEX文件
            Class dogClazz = pathClassLoader.loadClass("test/Dog");
            Class CatClazz = pathClassLoader.loadClass("test/Cat");
            Object dog =  dogClazz.getConstructors()[0].newInstance() ;
            dogClazz.getMethod("eat").invoke(dog) ;

            Object cat =  CatClazz.getConstructors()[0].newInstance() ;
            CatClazz.getMethod("eat").invoke(cat);

        }catch (Exception e){

        }

    }

方式2:DexClassLoader

private void loadClassDexClassLoader(){
		//app内部的
//        String dexPath = outputFile.getAbsolutePath(); 
		//外部存储环境
        String dexPath = Environment.getExternalStorageDirectory().getAbsolutePath()+"/1.dex";
        String optimizedPath = null ;
        String librarySearchPath = null;
        ClassLoader parentClassLoader = getClassLoader();

        DexClassLoader dexClassLoader = new DexClassLoader(dexPath, optimizedPath, librarySearchPath, parentClassLoader);

        try {
            // 使用 dexPath 来加载和处理DEX文件
            Class dogClazz = dexClassLoader.loadClass("test/Dog");
            Class CatClazz = dexClassLoader.loadClass("test/Cat");
            Object dog =  dogClazz.getConstructors()[0].newInstance() ;
            dogClazz.getMethod("eat").invoke(dog) ;

            Object cat =  CatClazz.getConstructors()[0].newInstance() ;
            CatClazz.getMethod("eat").invoke(cat);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

方式3:DexFile

 private void loadClassDexFile() {//< android 8
        try {
            // 加载DEX文件
            Context context = getApplicationContext();
            String dexDirectoryName = "dex"; // 存放DEX文件的子目录名称
            File dexDirectory = context.getDir(dexDirectoryName, Context.MODE_PRIVATE);
            String dexPath = new File(dexDirectory, "1.dex").getAbsolutePath();

            DexFile dexFile = new DexFile(dexPath); // 替换为实际的DEX文件路径

            // 获取DEX文件中的类名列表
            Enumeration<String> classNames = dexFile.entries();

            // 搜索目标类名
            String targetClassName = "test.Dog"; // 替换为目标类的完整类名
            Class<?> targetClass = null;

            while (classNames.hasMoreElements()) {
                String className = classNames.nextElement();
                if (className.equals(targetClassName)) {
                    targetClass = dexFile.loadClass(className, getClassLoader());
                    break;
                }
            }

            if (targetClass != null) {
                // 实例化目标类
                Object targetInstance = targetClass.newInstance();

                // 调用目标类的方法
                Method targetMethod = targetClass.getDeclaredMethod("eat"); // 替换为目标方法名
                targetMethod.setAccessible(true); // 如果方法是私有的,设置为可访问
                targetMethod.invoke(targetInstance);
            } else {
                // 没有找到目标类
                Log.d("Example", "Target class not found in DEX file");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

加载dex的3种方式的区别

相同点

  1. PathClassLoader、DexClassLoader 都是通过DexFile类来实现类加载。

  2. PathClassLoader、DexClassLoader 都会检测自己以及自己以上的类加载器是否已加载过这个类,如果已加载过,就直接将其返回,而不是重新加载;

3.PathClassLoader 和 DexClassLoader 类加载器都是继承自 BaseDexClassLoader

4.PathClassLoaderDexClassLoader 都能加载外部的 dex、jar、apk

不同点

DexClassLoader PathClassLoader 构造函数区别:参数optimizedDirectory的有无

optimizedDirectory 是经过优化的odex的路径。所谓odex,就是android软件中的classes.dex优化生成的odex文件,odex化即是把那个dex文件预先提取出来作用是能加快软件加载速度和开机速度。

android<8.0

  1. DexClassLoader 可以指定 optimizedDirectory (即 dex2oat 的产物 .odex 存放的位置),

  2. PathClassLoader 只能使用系统默认位置(optimizedDirectory)

  • BaseDexClassLoader

/**
@param dexPath:需要加载的文件列表 (文件可以是包含了 classes 和 resources 的 JAR、APK),多个文件用 “:” 分割。
@param optimizedDirectory:dex 经过 dex2oat 优化后,生成 .odex 文件的存放路径,可以为 null 。
		


@param libraryPath:存放需要加载的 native 库 (即.so库)的目录。
@param parent:父 ClassLoader。
通过构造函数我们大概可以了解到 BaseDexClassLoader 的运行方式,传入 dex 文件,然后进行优化,保存优化后的 dex 文件到 optimizedDirectory 目录。
**/
public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;

    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(parent);
        // 这里将 optimizedDirectory 传入了 DexPathList 中。
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    }
}
  • DexClassLoader

public class DexClassLoader extends BaseDexClassLoader {

    public DexClassLoader(String dexPath, String optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        // 此处将 optimizedDirectory 传入父类构造器中。
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}
  • PathClassLoader

public class PathClassLoader extends BaseDexClassLoader {

    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }

    public PathClassLoader(String dexPath, String libraryPath, ClassLoader parent) {
    	// // 此处第二个参数代表 optimizedDirectory,传入null,使用默认的路径(即系统指定的路径)。
        super(dexPath, null, libraryPath, parent);
    }
}

  • DexPathList

如果optimizedDirectory 如果不存在,直接创建 DexFile 对象。否则通过dex文件和 optimizedDirectory路径加载DexFile对象

final class DexPathList {
	// 省略代码

    // 保存 dex 和资源列表
    private final Element[] dexElements;
    // 保存 .so 库
    private final File[] nativeLibraryDirectories;

	// 省略代码
    
    public DexPathList(ClassLoader definingContext, String dexPath, 
    		String libraryPath, File optimizedDirectory) {
        // 省略代码
        // 这里将 optimizedDirectory 传递给了 makeDexElements 方法。
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions);
        
        // 省略代码
    }

    private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory,
                                             ArrayList<IOException> suppressedExceptions) {
        // 将 optimizedDirectory 参数传递给 loadDexFile 方法
        DexFile  dex = loadDexFile(file, optimizedDirectory);
        // 省略代码
    }

    private static DexFile loadDexFile(File file, File optimizedDirectory)
            throws IOException {
        if (optimizedDirectory == null) {
        	// optimizedDirectory 如果不存在,直接创建 DexFile 对象。
            return new DexFile(file);
        } else {
        	// 将dex/jar文件路径和输出目录转换为关联的优化dex文件的输出文件路径。
            String optimizedPath = optimizedPathFor(file, optimizedDirectory);
            // 将优化后的文件路径传入 DexFile 中。
            return DexFile.loadDex(file.getPath(), optimizedPath, 0);
        }
    }
}

android>8.0

没有用到optimizedDirectory ,DexClassLoader 和 PathClassLoader 没区别

public BaseDexClassLoader(String dexPath, File optimizedDirectory,
        String librarySearchPath, ClassLoader parent, boolean isTrusted) {
    super(parent);
    // optimizedDirectory 参数没有传递给 DexPathList。
    this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);

    if (reporter != null) {
        reportClassLoaderChain();
    }
}


评论