Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android熱補丁技術方案整理

Android熱補丁技術方案整理

編輯:關於Android編程

概述

項目快速迭代過程中,不可避免的出現BUG,Android線上出現問題,通常需要發版解決。緊急發版,用戶不一定升級,強制升級又不友好,有什麼更好的解決方案呢?這就用到了熱修復技術。

QQ團隊的hotfix

hotfix,後來發展成為RocooFix,

GitHub地址:https://github.com/dodola/HotFix

原理詳細介紹官方文章:安卓App熱補丁動態修復技術介紹

HotFix存在的問題:這種方法無法在已經加載好的類中實現動態替換,只能在類加載之前替換掉。就是說,補丁下載下來後,只能等待用戶重啟應用才能完成補丁效果。

RocooFix支持兩種模式:
靜態修復某種情況下需要重啟應用。
動態修復,無需重啟應用即可生效。

補丁制作

 

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

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

public class BugClass {

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

我們假設BugClass類裡的bug()方法出現錯誤,需要修復,修復代碼如下:

public class BugClass {

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

那麼我們只需要將修復過的類編譯後打包成dex即可

步驟如下:

將補丁類提取出來到一個文件夾裡
\

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

將jar包轉換成dex的jar包dx --dex --output=path_dex.jar path.jar

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

\

實現javassist動態代碼注入

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

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

問題原因在文檔中已經描述的比較清楚。

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

很明顯,解決的方法就是在類中引用一個其他dex中的類,但是源碼方式的引用會將引用的類打入同一個dex中,所以我們需要找到一種既能編譯通過並且將兩個互相引用的類分離到不同的dex中,於是就有了這個動態的代碼植入方式。

首先我們需要制作引用類的dex包,代碼在hackdex中,我直接使用了文檔中的類名AntilazyLoad這樣可以和文章中對應起來,方便一些。

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

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

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

這個項目是使用Groovy開發的,需要配置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)

        //下面的操作比較容易理解,在將需要關聯的類的構造方法中插入引用代碼
        CtClass c = classes.getCtClass("dodola.hotfix.BugClass")
        println("====添加構造方法====")
        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("====添加構造方法====")
        def constructor1 = c1.getConstructors()[0];
        constructor1.insertBefore("System.out.println(dodola.hackdex.AntilazyLoad.class);")
        c1.writeFile(buildDir)


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

下面在代碼編譯完成,打包之前,執行植入代碼的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')//第二個參數是hackdex的class所在目錄

}

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

反編譯編譯後的apk可以發現,代碼已經植入進去,而且包裡並不存在dodola.hackdex.AntilazyLoad這個類

\


阿裡巴巴的AndFix

GitHub地址:https://github.com/alibaba/AndFix

使用步驟

初始化

patchManager = new PatchManager(context);
patchManager.init(appversion);//current version
加載patch
patchManager.loadPatch();
添加patch文件
patchManager.addPatch(path);
支持熱更新,不需要重新啟動

 

阿裡巴巴的dexposed

GitHub地址:https://github.com/alibaba/dexposed

大眾點評的Nuwa

Nuwa的具體實現也是根據QQ空間的熱修復方案來實現的

GitHub地址:https://github.com/jasonross/Nuwa

DroidFix

GitHub地址:https://github.com/bunnyblue/DroidFix

官方介紹:http://bunnyblue.github.io/DroidFix/

DroidFix的實現原理跟QQ空間的熱補丁方案類似。

攜程的DynamicAPK

GitHub地址:https://github.com/CtripMobile/DynamicAPK   主流熱補丁開源方案基本上就是以上這些。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved