溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android 代碼熱修復詳解

發(fā)布時間:2020-05-31 10:42:26 來源:網(wǎng)絡(luò) 閱讀:733 作者:home_xu 欄目:移動開發(fā)

java:類加載原理:
當類加載器收到加載類或資源的請求時,通常都是先委托給父類加載器加載,也就是說只有當父類加載器找不到指定類或資源時,自身才會執(zhí)行實際的類加載過程,具體的加載過程如下:

1、源 ClassLoader 先判斷該 Class 是否已加載,如果已加載,則直接返回 Class,如果沒有則委托給父類加載器。

2、父類加載器判斷是否加載過該 Class,如果已加載,則直接返回 Class,如果沒有則委托給祖父類加載器。

3、依此類推,直到始祖類加載器(引用類加載器)。

4、始祖類加載器判斷是否加載過該 Class,如果已加載,則直接返回 Class,如果沒有則嘗試從其對應(yīng)的類路徑下尋找 class 字節(jié)碼文件并載入。如果載入成功,則直接返回 Class,如果載入失敗,則委托給始祖類加載器的子類加載器。
5、始祖類加載器的子類加載器嘗試從其對應(yīng)的類路徑下尋找 class 字節(jié)碼文件并載入。如果載入成功,則直接返回 Class,如果載入失敗,則委托給始祖類加載器的孫類加載器。

6、依此類推,直到源 ClassLoader。

7、源 ClassLoader 嘗試從其對應(yīng)的類路徑下尋找 class 字節(jié)碼文件并載入。如果載入成功,則直接返回 Class,如果載入失敗,源 ClassLoader 不會再委托其子類加載器,而是拋出異常

Android類加載:
Android中的類加載器是BootClassLoader、PathClassLoader、DexClassLoader,其中BootClassLoader是虛擬機加載系統(tǒng)類需要用到的,PathClassLoader是App加載自身dex文件中的類用到的,DexClassLoader可以加載直接或間接包含dex文件的文件。

PathClassLoader和DexClassLoader都繼承自BaseDexClassLoader,它的一個DexPathList類型的成員變量pathList很重要。DexPathList中有一個Element類型的數(shù)組dexElements,這個數(shù)組中存放了包含dex文件(對應(yīng)的是DexFile)的元素。BaseDexClassLoader加載一個類,最后調(diào)用的是DexFile的方法進行加載的

下面是代碼熱修復的兩種方式:
1、根據(jù)類加載為父委托加載原理 將加載修復后的dex的DexClassLoader插入到PathClassLoader和BootstrapClassLoader中間,也就是將DexClassLoader設(shè)置為PathClassLoader的父加載器,將BootstrapClassLoader設(shè)置為DexClassLoader 順序為:BootstrapClassLoader--->DexClassLoader--->PathClassLoader

//根據(jù)類加載為父委托加載原理   替換有bug的類放在dexPath中讓DexClassLoader優(yōu)先加載
    //類加載順序: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);//設(shè)置當前類加載器的父加載器為dexClassLoader
    }

 /**
     * @param clazz
     * @param fieldName 屬性名稱
     * @param target    要設(shè)置屬性的對象
     * @param value     設(shè)置屬性的值
     * @return 設(shè)置成功
     */
    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;//設(shè)置失敗
    }

2、根據(jù)安卓的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) {
            //獲取對應(yīng)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);
                //將合并的值設(shè)置給當前的pathClassLoader的Element[]中
                this.setField(dexPathList1.getClass(), this.mElementFieldName, dexPathList1, finalElements);
                Log.e("HotFixEngine", "loadPatchDex2: success");
            }
        }

    }

    /**
     * 獲取對應(yīng)屬性值
     * @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;
    }

    /**
     * 兩個數(shù)組合并
     *
     * @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

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI