Android 代碼熱修複詳解

來源:互聯網
上載者:User

標籤: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 代碼熱修複詳解

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.