1. 程式人生 > >android熱修復——自己做個熱修復

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); } }