安卓App熱補丁動態(tài)修復(fù)實現(xiàn)

前兩天看到QQ空間發(fā)了一篇動態(tài)熱補丁的文章幕屹,昨天晚上實現(xiàn)了一個波势。
項目地址:https://github.com/dodola/HotFix

HotFix

安卓App熱補丁動態(tài)修復(fù)框架

介紹

該項目是基于QQ空間終端開發(fā)團隊的技術(shù)文章實現(xiàn)的肮帐,完成了文章中提到的基本功能铺根。

文章地址:安卓App熱補丁動態(tài)修復(fù)技術(shù)介紹

項目部分代碼從 dalvik_patch 項目中修改而來胚股,這個項目本來是用來實現(xiàn)multidex的庭再,發(fā)現(xiàn)可以用來實現(xiàn)方法替換的效果雷客。

項目包括核心類庫芒珠,補丁制作庫,例子搅裙≈遄浚可以直接運行代碼看效果。

詳細說明

補丁制作

該技術(shù)的原理很簡單部逮,其實就是用ClassLoader加載機制娜汁,覆蓋掉有問題的方法。所以我們的補丁其實就是有問題的類打成的一個包兄朋。

例子中的出現(xiàn)問題的類是 dodola.hotfix.BugClass
原始代碼如下:

public class BugClass {

    public String bug() {
        return "bug class";
    }
}

我們假設(shè)BugClass類里的bug()方法出現(xiàn)錯誤掐禁,需要修復(fù),修復(fù)代碼如下:


public class BugClass {

    public String bug() {
        return "fixed class";
    }
}

那么我們只需要將修復(fù)過的類編譯后打包成dex即可

步驟如下:

  1. 將補丁類提取出來到一個文件夾里

    patch1.png

  2. 將class文件打入一個jar包中 jar cvf path.jar *

  3. 將jar包轉(zhuǎn)換成dex的jar包 dx --dex --output=path_dex.jar path.jar

這樣就生成了補丁包path_dex.jar

patch2.png

實現(xiàn)javassist動態(tài)代碼注入

實現(xiàn)這一部分功能的原因主要是因為出現(xiàn)如下異常

java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation

問題原因在文檔中已經(jīng)描述的比較清楚蜈漓。

就是如果以上方法中直接引用到的類(第一層級關(guān)系穆桂,不會進行遞歸搜索)和clazz都在同一個dex中的話,那么這個類就會被打上CLASS_ISPREVERIFIED

很明顯融虽,解決的方法就是在類中引用一個其他dex中的類享完,但是源碼方式的引用會將引用的類打入同一個dex中,所以我們需要找到一種既能編譯通過并且將兩個互相引用的類分離到不同的dex中有额,于是就有了這個動態(tài)的代碼植入方式般又。

首先我們需要制作引用類的dex包彼绷,代碼在hackdex中,我直接使用了文檔中的類名 AntilazyLoad 這樣可以和文章中對應(yīng)起來茴迁,方便一些寄悯。

我們將這個庫打包成dex的jar包,方法跟制作補丁一樣堕义。

下面是重點猜旬,我們要用javassist將這個類在編譯打包的過程中插入到目標(biāo)類中。

為了方便倦卖,我將這個過程做成了一個Gradle的Task洒擦,代碼在buildSrc中。

這個項目是使用Groovy開發(fā)的怕膛,需要配置Groovy SDK才可以編譯成功熟嫩。

核心代碼如下:

 /**
     * 植入代碼
     * @param buildDir 是項目的build class目錄,就是我們需要注入的class所在地
     * @param lib 這個是hackdex的目錄,就是AntilazyLoad類的class文件所在地
     */
    public static void process(String buildDir, String lib) {

        println(lib)
        ClassPool classes = ClassPool.getDefault()
        classes.appendClassPath(buildDir)
        classes.appendClassPath(lib)

        //下面的操作比較容易理解,在將需要關(guān)聯(lián)的類的構(gòu)造方法中插入引用代碼
        CtClass c = classes.getCtClass("dodola.hotfix.BugClass")
        println("====添加構(gòu)造方法====")
        def constructor = c.getConstructors()[0];
        constructor.insertBefore("System.out.println(dodola.hackdex.AntilazyLoad.class);")
        c.writeFile(buildDir)



        CtClass c1 = classes.getCtClass("dodola.hotfix.LoadBugClass")
        println("====添加構(gòu)造方法====")
        def constructor1 = c1.getConstructors()[0];
        constructor1.insertBefore("System.out.println(dodola.hackdex.AntilazyLoad.class);")
        c1.writeFile(buildDir)


        growl("ClassDumper", "${c.frozen}")
    }

下面在代碼編譯完成,打包之前褐捻,執(zhí)行植入代碼的task就可以了掸茅。

在 app 項目的 build.gradle 中插入如下代碼

task('processWithJavassist') << {
    String classPath = file('build/intermediates/classes/debug')//項目編譯class所在目錄
    dodola.patch.PatchClass.process(classPath, project(':hackdex').buildDir
            .absolutePath + '/intermediates/classes/debug')//第二個參數(shù)是hackdex的class所在目錄

}

android{
   .......
    applicationVariants.all { variant ->
        variant.dex.dependsOn << processWithJavassist //在執(zhí)行dx命令之前將代碼打入到class中
    }
}

反編譯編譯后的apk可以發(fā)現(xiàn),代碼已經(jīng)植入進去柠逞,而且包里并不存在dodola.hackdex.AntilazyLoad 這個類

patch3.png

ISSUE

開發(fā)測試過程中遇到一些問題昧狮,這種方法無法在已經(jīng)加載好的類中實現(xiàn)動態(tài)替換,只能在類加載之前替換掉板壮。就是說陵且,補丁下載下來后,只能等待用戶重啟應(yīng)用才能完成補丁效果个束。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市聊疲,隨后出現(xiàn)的幾起案子茬底,更是在濱河造成了極大的恐慌,老刑警劉巖获洲,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件阱表,死亡現(xiàn)場離奇詭異,居然都是意外死亡贡珊,警方通過查閱死者的電腦和手機最爬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來门岔,“玉大人爱致,你說我怎么就攤上這事『妫” “怎么了糠悯?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵帮坚,是天一觀的道長。 經(jīng)常有香客問我互艾,道長试和,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任纫普,我火速辦了婚禮阅悍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘昨稼。我一直安慰自己节视,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布悦昵。 她就那樣靜靜地躺著肴茄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪但指。 梳的紋絲不亂的頭發(fā)上寡痰,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機與錄音棋凳,去河邊找鬼拦坠。 笑死,一個胖子當(dāng)著我的面吹牛剩岳,可吹牛的內(nèi)容都是我干的贞滨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼拍棕,長吁一口氣:“原來是場噩夢啊……” “哼晓铆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起绰播,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤骄噪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蠢箩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體链蕊,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年谬泌,在試婚紗的時候發(fā)現(xiàn)自己被綠了滔韵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡掌实,死狀恐怖陪蜻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情潮峦,我是刑警寧澤囱皿,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布勇婴,位于F島的核電站,受9級特大地震影響嘱腥,放射性物質(zhì)發(fā)生泄漏耕渴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一齿兔、第九天 我趴在偏房一處隱蔽的房頂上張望橱脸。 院中可真熱鬧,春花似錦分苇、人聲如沸添诉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽栏赴。三九已至,卻和暖如春靖秩,著一層夾襖步出監(jiān)牢的瞬間须眷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工沟突, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留花颗,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓惠拭,卻偏偏與公主長得像扩劝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子职辅,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容