Tinker热修复核心实现

预备工作

自己打好一个修复好的dex包,命名为out.dex,然后放大sd卡目录下

将out.dex拷贝到app的odex目录下

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void test(View v) {
        Test test = new Test();
        test.testFix(this);
    }

    public void fix(View v) {
        fixBug();
    }

    //将修复好的out.dex拷贝到odex目录下
    private void fixBug() {
        File filesDir = this.getDir("odex", Context.MODE_PRIVATE);
        String name = "out.dex";
        String filePath = new File(filesDir, name).getAbsolutePath();
        File file = new File(filePath);
        if (file.exists()) {
            file.delete();
        }
        InputStream is = null;
        FileOutputStream os = null;
        try {
            Log.i(TAG, "fixBug: " + new File(Environment.getExternalStorageDirectory(), name).getAbsolutePath());
            is = new FileInputStream(new File(Environment.getExternalStorageDirectory(), name));
            os = new FileOutputStream(filePath);
            int len = 0;
            byte[] buffer = new byte[1024];
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
            File f = new File(filePath);
            if (f.exists()) {
                Toast.makeText(this, "dex overwrite", Toast.LENGTH_SHORT).show();
            }
            FixManager.loadDex(this);
//            FixDexUtils.loadFixedDex(this);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                os.close();
                is.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

将odex中的修复包设置到dexElements当中

public class FixManager {
    public static final String DEX_DIR = "odex";
    //  用于存储我们的修复包dex
    private static HashSet loadedDex = new HashSet();

    static{
        loadedDex.clear();
    }
    public static void loadDex(Context context) {
        if (context == null) {
            return;
        }
        //获取app目录下的odex文件
        File filesDir = context.getDir("odex", Context.MODE_PRIVATE);
        //准备遍历odex下的文件
        File[]  listFiles=filesDir.listFiles();
        for (File file : listFiles) {
            //如果是classes或者是.dex结尾则是我们需要的修复包
            if(file.getName().startsWith("classes")||file.getName().endsWith(".dex")){
                Log.i("INFO", "dexName:"+file.getName());
                loadedDex.add(file);
            }
        }
        //定义一个历史文件
        String optimizeDir = filesDir.getAbsolutePath() + File.separator + "opt_dex";
        File fopt = new File(optimizeDir);
        if (!fopt.exists()) {
            fopt.mkdirs();
        }
        //开始将修复包dex设置到dexElements数组当中
        for (File dex : loadedDex) {
            //我们通过创建一个DexClassLoader 加载我们的out.dex,将其存入DexClassLoader中的Elements数组当中,这个数组和PathClassLoader中的Elements不是同一个
            //我们后面要做的便是将这两个数组进行合并,然后重新设置到PathClassLoader当中
            DexClassLoader classLoader = new DexClassLoader(dex.getAbsolutePath(), fopt.getAbsolutePath(), null, context.getClassLoader());
            PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();


            try {
                //-----------------------系统的ClassLoader------------------------------------
                Class baseDexClazzLoader=Class.forName("dalvik.system.BaseDexClassLoader");
                Field  pathListFiled=baseDexClazzLoader.getDeclaredField("pathList");
                pathListFiled.setAccessible(true);
                Object pathListObject = pathListFiled.get(pathClassLoader);

                Class  systemPathClazz=pathListObject.getClass();
                Field  systemElementsField = systemPathClazz.getDeclaredField("dexElements");
                systemElementsField.setAccessible(true);
                Object systemElements=systemElementsField.get(pathListObject);


                //------------------自己的ClassLoader--------------------------
                Class myDexClazzLoader=Class.forName("dalvik.system.BaseDexClassLoader");
                Field  myPathListFiled=myDexClazzLoader.getDeclaredField("pathList");
                myPathListFiled.setAccessible(true);
                Object myPathListObject =myPathListFiled.get(classLoader);

                Class  myPathClazz=myPathListObject.getClass();
                Field  myElementsField = myPathClazz.getDeclaredField("dexElements");
                myElementsField.setAccessible(true);
                Object myElements=myElementsField.get(myPathListObject);

                //------------------------融合-----------------------------
                Class sigleElementClazz = systemElements.getClass().getComponentType();
                int systemLength = Array.getLength(systemElements);
                int myLength = Array.getLength(myElements);
                int newSystenLength = systemLength + myLength;
                //生成一个新的 数组   类型为Element类型
                Object newElementsArray = Array.newInstance(sigleElementClazz, newSystenLength);
                for (int i = 0; i < newSystenLength; i++) {
                    if (i < myLength) {
                        Array.set(newElementsArray, i, Array.get(myElements, i));
                    }else {
                        Array.set(newElementsArray, i, Array.get(systemElements, i - myLength));
                    }
                }
                //---------------------------融合完毕,将新数组放到系统的PathLoad内部---------------------------------
                Field  elementsField=pathListObject.getClass().getDeclaredField("dexElements");;
                elementsField.setAccessible(true);

                elementsField.set(pathListObject,newElementsArray);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

因为逻辑不是很复杂,上述注释都已经标注,关于odex是什么,干什么用的可以google一下,这边不做说明.

通过上面几个简单的逻辑,实际上我们就已经实现了Tinker的核心了。

你可能感兴趣的:(Tinker热修复核心实现)