1. 程式人生 > >ClassLoader方式的實現以及基本原理

ClassLoader方式的實現以及基本原理

  1. 一些基本知識

    1. 在Apk被安裝的時候,虛擬機器會將dex檔案提取轉化為odex檔案

      這個操作叫做 dex optimize ,就是dex檔案優化的意思,是用於提高程式執行效能的。

    2. 那麼在 dex optimize 這一個過程中,如果虛擬機器的 verify 選項為true的時候,就會進行類校驗。

      boolean flag = doVerifyClass(clazz)
      

      這個校驗具體過程就是,如果 clazz 以及和其有關聯的類在同一個classes.dex檔案中,
      那麼這個 clazz 將會被標記 Flag 為 CLASS_ISPREVERIFIED。

      反之,則不會被標記Flag。

    3. 那麼當應用程式執行(find class –> dvm resolve class)的時候

      虛擬機器會再次校驗clazz

      IS_CLASS_FLAG_SET(referrer,CLASS_ISPREVERIFIED);
      

      這個過程具體就是,如果clazz被標記為CLASS_ISPREVERIFIED,那麼就校驗:

      當前clazz 以及 referrer(和其有關聯的 clazz )是否在同一個classes.dex檔案中
      如果不在同一個classes.dex,那麼就會報錯。

    4. 總結:

      reading the fucking source code

      一次校驗(dex optimize):
      如果 clazz 以及 referrer 同屬一個classes.dex,那麼clazz的Flag被標記為CLASS_ISPREVERIFIED。

      二次校驗 (dvm resolve class):
      如果 clazz 的Flag為CLASS_ISPREVERIFIED,且referrer同屬一個classes.dex。

  2. 實現框架

    1. QQ空間超級補丁原理

      • 首先,將修復的BUG的class打包為patch.dex

      • 然後,基於dex的載入原理

        在載入Dex的時候,將這個patch.dex插入到dexEelements陣列的最前面

        這個時候,同一個類在不同的2個dex檔案中都有
        如果前面的dex檔案已經載入過,後面dex檔案中同名的類則不會被載入。

      • 最後,還需要解決class二次驗證報錯的問題

        即讓class不被標記為CLASS_ISPREVERFIED,那麼dvm就不會二次校驗class。

        不讓class被標記為CLASS_ISPREVERFIED的解決方案

        在除了Application之外的class的建構函式中注入hack程式碼

        if (ClassVerifier.PREVENT_VERIFY) {
            System.out.println(AntilazyLoad.class);
        }
        

        那麼,如何在所有類的建構函式中新增上面的程式碼?

        1. 在原始碼中直接新增,編譯不通過,會提示找不到這個類,所以這個不行。
        2. 編譯之後,在 .**class檔案被打包為.**dex檔案之前,對位元組碼檔案中注入hack程式碼
          QQ空間方案使用的就是javassist操作位元組碼,注入程式碼。

        需要注意的是,Application作為程式的入口,其構造方法不能注入hack程式碼

        因為,我們需要和MutilDex分包方案一樣
        在Application的onCreate()方法中載入hack.dex檔案到dexPathList中
        如果在Application構造方法中注入了hack程式碼,就會出現找不到類而Crash。

      • 總結

        1. 方案基於MutilDex分包原理
        2. 但是和分包原理有差異,需要解決差異帶來的一系列問題
        3. 差異就是MutilDex分包不會有同名的類
          而QQ空間方案需要載入patch.dex,會產生同名類
        4. javassist位元組碼注入技術解決差異問題,以及載入hack.dex
        5. 在Android studio中 編譯打包都是自動化的
          如何注入位元組碼需要了解Gradle構建、groovy語言

        QQ空間超級補丁存在的問題:

        1. 由於需要避開Android虛擬機器的校驗優化機制,這些校驗機制都是有意義的
          如果繞開的話,會造成一定的效能損耗。
          採用插樁導致所有類都非preverify,這導致verify與optimize操作會在載入類時觸發。這會有一定的效能損耗,特別是載入大量的類的情況,影響比較大。
        2. 在ART虛擬機器,如果補丁類修改了類變數或者方法,可能會導致出現記憶體地址錯亂。
          為了解決這個問題,需要將這種補丁類的父類以及呼叫了這個類的所有類都加入到補丁包中,這可能帶來補丁包大小的急劇真加。
    2. 微信Tinker Dex補丁原理

      需要了解Dex檔案結構和原理
      需要了解DexDiff演算法原理
      需要了解MutilDex Install原理

      • Tinker Dex補丁原理圖

      • Tinker 載入Dex補丁流程圖

      • Tinker 重要的部分DexDiff,比較複雜,有時間在做了解