標籤:set 引用 his pcl tco param tde 兩種 成功
java:類載入原理:
當類載入器收到載入類或資源的請求時,通常都是先委託給父類載入器載入,也就是說只有當父類載入器找不到指定類或資源時,自身才會執行實際的類載入過程,具體的載入過程如下:
1、源 ClassLoader 先判斷該 Class 是否已載入,如果已載入,則直接返回 Class,如果沒有則委託給父類載入器。
2、父類載入器判斷是否載入過該 Class,如果已載入,則直接返回 Class,如果沒有則委託給祖父類載入器。
3、依此類推,直到始祖類載入器(引用類載入器)。
4、始祖類載入器判斷是否載入過該 Class,如果已載入,則直接返回 Class,如果沒有則嘗試從其對應的類路徑下尋找 class 位元組碼檔案並載入。如果載入成功,則直接返回 Class,如果載入失敗,則委託給始祖類載入器的子類載入器。
5、始祖類載入器的子類載入器嘗試從其對應的類路徑下尋找 class 位元組碼檔案並載入。如果載入成功,則直接返回 Class,如果載入失敗,則委託給始祖類載入器的孫類載入器。
6、依此類推,直到源 ClassLoader。
7、源 ClassLoader 嘗試從其對應的類路徑下尋找 class 位元組碼檔案並載入。如果載入成功,則直接返回 Class,如果載入失敗,源 ClassLoader 不會再委託其子類載入器,而是拋出異常
Android類載入:
Android中的類載入器是BootClassLoader、PathClassLoader、DexClassLoader,其中BootClassLoader是虛擬機器載入系統類別需要用到的,PathClassLoader是App載入自身dex檔案中的類用到的,DexClassLoader可以載入直接或間接包含dex檔案的檔案。
PathClassLoader和DexClassLoader都繼承自BaseDexClassLoader,它的一個DexPathList類型的成員變數pathList很重要。DexPathList中有一個Element類型的數組dexElements,這個數組中存放了包含dex檔案(對應的是DexFile)的元素。BaseDexClassLoader載入一個類,最後調用的是DexFile的方法進行載入的
下面是代碼熱修複的兩種方式:
1、根據類載入為父委託載入原理 將載入修複後的dex的DexClassLoader插入到PathClassLoader和BootstrapClassLoader中間,也就是將DexClassLoader設定為PathClassLoader的父載入器,將BootstrapClassLoader設定為DexClassLoader 順序為:BootstrapClassLoader--->DexClassLoader--->PathClassLoader
//根據類載入為父委託載入原理 替換有bug的類放在dexPath中讓DexClassLoader優先載入 //類載入順序:BootstrapClassLoader---->DexClassLoader----->PathClassLoader public void loadPatchDex(Context context, String dexPath, String optimizedDirectory, String librarySearchPath) { ClassLoader currentClassLoader = context.getClassLoader();//context載入器(PathClassLoader) ClassLoader parentClassLoader = currentClassLoader.getParent();//context的父載入器(BootstrapClassLoader) //載入dexPath載入器 DexClassLoader dexClassLoader = new DexClassLoader(dexPath, optimizedDirectory, librarySearchPath, parentClassLoader); this.setField(ClassLoader.class, this.mParentFieldName, currentClassLoader, dexClassLoader);//設定當前類載入器的父載入器為dexClassLoader } /** * @param clazz * @param fieldName 屬性名稱 * @param target 要設定屬性的對象 * @param value 設定屬性的值 * @return 設定成功 */ private boolean setField(Class clazz, String fieldName, Object target, Object value) { try { Field field = clazz.getDeclaredField(fieldName); if (field != null) { field.setAccessible(true); field.set(target, value); } return true; } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return false;//設定失敗 }
2、根據安卓的PathClassLoader載入機制將DexClassLoader載入的新的Dex添加的DexPathList的dexElements屬性列表中,將更新的dex載入放在dexElements清單索引之前
public void loadPatchDex2(Context context, String dexPath, String optimizedDirectory, String librarySearchPath) { ClassLoader pathClassLoader = context.getClassLoader(); //載入dexPath載入器 DexClassLoader dexClassLoader = new DexClassLoader(dexPath, optimizedDirectory, librarySearchPath, pathClassLoader.getParent()); //擷取BaseDexClassLoader的DexPathList屬性 私人的需要通過反射擷取 Object dexPathList1 = this.getFieldValue(BaseDexClassLoader.class, this.mDexPathListFieldName, pathClassLoader);//pathClassLoader的PathList屬性 Object dexPathList2 = this.getFieldValue(BaseDexClassLoader.class, this.mDexPathListFieldName, dexClassLoader); if (dexPathList1 != null && dexPathList2 != null) { //擷取對應pathList中的dexElements屬性 Object dexElements1 = this.getFieldValue(dexPathList1.getClass(), this.mElementFieldName, dexPathList1);//當前PathClassLoader中的Element[]屬性 Object dexElements2 = this.getFieldValue(dexPathList2.getClass(), this.mElementFieldName, dexPathList2);//DexClassLoader中的Element[]屬性 if (dexElements1 != null && dexElements2 != null) { //將兩個Element[]屬性合并 Object finalElements = combineArray(dexElements2, dexElements1); //將合并的值設定給當前的pathClassLoader的Element[]中 this.setField(dexPathList1.getClass(), this.mElementFieldName, dexPathList1, finalElements); Log.e("HotFixEngine", "loadPatchDex2: success"); } } } /** * 擷取對應屬性值 * @param clazz * @param fieldName 屬性名稱 * @param target 要操作的對象 * @return */ private Object getFieldValue(Class clazz, String fieldName, Object target) { Field field = this.getField(clazz, fieldName); if (field != null) { try { field.setAccessible(true); return field.get(target); } catch (IllegalAccessException e) { e.printStackTrace(); } } return null; } private Field getField(Class clazz, String fieldName) { try { Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); return field; } catch (NoSuchFieldException e) { e.printStackTrace(); } return null; } /** * 兩個數組合并 * * @param arrayLhs * @param arrayRhs * @return */ private static Object combineArray(Object arrayLhs, Object arrayRhs) { Class<?> localClass = arrayLhs.getClass().getComponentType(); int i = Array.getLength(arrayLhs); int j = i + 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(arrayRhs, k - i)); } } return result; }
Demo地址:https://github.com/xuguohongai/android/tree/master/AndroidHotFixDemo
Android 代碼熱修複詳解