欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android热修复制作的实例讲解(代码教程)

程序员文章站 2022-07-05 22:46:47
类的加载机制 需要注意的地方 1.每次生成之后一定要测试; 2.尽量的不要分包,不要分多个dex 3.混淆的时候,设计到NDK AndFix.java 不要混淆 4.生成包...

类的加载机制

android热修复制作的实例讲解(代码教程)

需要注意的地方

1.每次生成之后一定要测试; 2.尽量的不要分包,不要分多个dex 3.混淆的时候,设计到NDK AndFix.java 不要混淆 4.生成包之后一般会加固什么的,这个时候生成的差分包,一定要在之前去生成。 5.既然是去修复方法,第一个不能增加成员变量,不能增加方法

源码

public class FixDexManager {
    private Context mContext;
    private File mDexFile;
    private static final String TAG = FixDexManager.class.getSimpleName();
    private Object applicationDexElements;

    public FixDexManager(Context context) {
        this.mContext = context;
        //可以访问的dex目录
        mDexFile = context.getDir("odex", Context.MODE_PRIVATE);
    }

    /**
     * 修复dex包
     */
    public void fixDex(String fixDexPath) throws Exception {

        //2.获取下载好的补丁 dexElement
        //2.1移动到系统能够访问的 dex目录下
        File srcFile = new File(fixDexPath);
        if (!srcFile.exists()) {
            throw new FileNotFoundException(fixDexPath);
        }
        File destFile = new File(mDexFile, srcFile.getName());
        if (destFile.exists()) {
            Log.d(TAG, "patch [" + fixDexPath + "] has be loaded.");
            return;
        }
        copyFile(srcFile, destFile);
        //2.2ClassLoader读取fileDex路径
        List fixDexFiles = new ArrayList<>();
        fixDexFiles.add(destFile);


    }

    /**
     * 把dexElement注入到classLoader
     */
    private void injectDexElement(ClassLoader classLoader, Object dexElements) throws Exception {
        //1.先获取pathList
        Field pathListField = BaseDexClassLoader.class.getDeclaredField("pathList");
        //所有属性都可以获得
        pathListField.setAccessible(true);
        Object pathList = pathListField.get(classLoader);
        //2.获得pathList中的dexElements
        Field dexElementsFiled = pathList.getClass().getDeclaredField("dexElements");
        dexElementsFiled.setAccessible(true);
        //注入
        dexElementsFiled.set(dexElementsFiled.get(pathList), dexElements);
    }

    /**
     * 合并数组
     */
    private static Object combineArray(Object arrayLhs, Object arrayRhs) {
        Class localClass = arrayLhs.getClass().getComponentType();//获得数组对象
        int i = Array.getLength(arrayLhs);
        int j = Array.getLength(arrayRhs);
        Object result = Array.newInstance(localClass, j);
        for (int k = 0; k < j; ++k) {
            if (k < i) {
                Array.set(result, k, Array.get(arrayLhs, k));
            } else {
                Array.set(result, k, Array.get(arrayLhs, k - 1));
            }
        }
        return result;
    }

    /**
     * 将路径复制到目标文件下
     */
    public static void copyFile(File src, File dest) throws IOException {
        FileChannel inChannel = null;
        FileChannel outChannel = null;
        try {
            if (!dest.exists()) {
                dest.createNewFile();
            }
            inChannel = new FileInputStream(src).getChannel();
            outChannel = new FileOutputStream(dest).getChannel();
            inChannel.transferTo(0, inChannel.size(), outChannel);
        } finally {
            if (inChannel != null) {
                inChannel.close();
            }
            if (outChannel != null) {
                outChannel.close();
            }
        }
    }

    /**
     * 从classLoader中获取
     *
     * @param classLoader
     */
    private Object getDexElementsByClassLoader(ClassLoader classLoader) throws Exception {
        //1.先获取pathList
        Field pathListField = BaseDexClassLoader.class.getDeclaredField("pathList");
        //所有属性都可以获得
        pathListField.setAccessible(true);
        Object pathList = pathListField.get(classLoader);
        //2.获得pathList中的dexElements
        Field dexElementsFiled = pathList.getClass().getDeclaredField("dexElements");
        dexElementsFiled.setAccessible(true);
        return dexElementsFiled.get(pathList);
    }

    /**
     * 加载全部的修复包
     */
    public void loadFixDex() throws Exception {
        File[] dexFiles = mDexFile.listFiles();
        List fixDexFiles = new ArrayList<>();
        for (File dexFile : dexFiles) {
            if (dexFile.getName().endsWith(".dex")) {
                fixDexFiles.add(dexFile);
            }
        }
        fixDexFiles(fixDexFiles);
    }

    /**
     * 修复dex
     */
    private void fixDexFiles(List fixDexFiles) throws Exception {
        //1.先获取已运行的dexElement
        ClassLoader applicationClassLoader = mContext.getClassLoader();
        Object dexElements = getDexElementsByClassLoader(applicationClassLoader);
        File optimizedDirectory = new File(mDexFile, "odex");
        if (!optimizedDirectory.exists()) {
            optimizedDirectory.mkdir();
        }
        //修复
        for (File fixDexFile : fixDexFiles) {

            ClassLoader fixDexClassLoader = new BaseDexClassLoader(
                    fixDexFile.getAbsolutePath(),//dex路径 必须要在应用目录下的dex文件中
                    optimizedDirectory,//解压路径
                    null,//.so文件的位置
                    applicationClassLoader//父classloader
            );
            Object fixDexElement = getDexElementsByClassLoader(fixDexClassLoader);

            //3.把补丁的dexElement插到已经运行好的dexElements的最前面
            //合并数组
            applicationDexElements = combineArray(dexElements, fixDexElement);

        }
        //注入到原来的类中applicationClassLoader
        injectDexElement(applicationClassLoader, applicationDexElements);
    }
}