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<File> 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<File> fixDexFiles = new ArrayList<>();
        for (File dexFile : dexFiles) {
            if (dexFile.getName().endsWith(".dex")) {
                fixDexFiles.add(dexFile);
            }
        }
        fixDexFiles(fixDexFiles);
    }

    /**
     * 修复dex
     */
    private void fixDexFiles(List<File> 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);
    }
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值