1. 程式人生 > >熱修復系列之一----Android 熱修復原理篇及幾大方案比較

熱修復系列之一----Android 熱修復原理篇及幾大方案比較

熱修復說白了就是”即時無感打補丁”,比如你們公司上線一個app,使用者反應有重大bug,需要緊急修復。2015年以來,Android開發領域裡對熱修復技術的討論和分享越來越多,同時也出現了一些不同的解決方案.如果按照通常做法,那就是程式猿加班搞定bug,然後測試,重新打包併發布。這樣帶來的問題就是成本高,效率低。於是,熱修復就應運而生.一般通過事先設定的介面從網上下載無Bug的程式碼來替換有Bug的程式碼。這樣就省事多了,用 戶體驗也好。目前熱修復儘管有很多坑,做了好多工作,可能吃力不討好,各種適配可能還是沒修復線上的有些Bug。不過呢,對於一個產品有熱修復畢竟是件好事。尤其是對於一個有眾多使用者的app(,一個bug不只是影響到幾個幾十個使用者,一些創業公司的APP,崩潰或者bug可能直接導致使用者解除安裝和永不使用,所以,就衝它有不用發版也可以解決我們線上的bug,我們的app也要適當考慮加入熱修復。

我們知道Android系統也是仿照java搞了一個虛擬機器,不過它不叫JVM,它叫Dalvik/ART VM他們還是有很大區別的(這是不是我們的重點, 點開是個拓展閱讀)。我們只需要知道,Dalvik/ART VM 虛擬機器載入類和資源也是要用到ClassLoader,不過Jvm通過ClassLoader載入的class位元組碼,而Dalvik/ART VM通過ClassLoader載入則是dex。

Android的類載入器分為兩種,PathClassLoader和DexClassLoader,兩者都繼承自BaseDexClassLoader

PathClassLoader

程式碼位於libcore\dalvik\src\main\Java\dalvik\system\PathClassLoader.java 
DexClassLoader程式碼位於libcore\dalvik\src\main\java\dalvik\system\DexClassLoader.java 
BaseDexClassLoader程式碼位於libcore\dalvik\src\main\java\dalvik\system\BaseDexClassLoader.java

  • PathClassLoader
  • 用來載入系統類和應用類

  • DexClassLoader

    用來載入jar、apk、dex檔案.載入jar、apk也是最終抽取裡面的Dex檔案進行載入.

    這裡寫圖片描述

2.熱修復機制

熱修復就是利用dexElements的順序來做文章,當一個補丁的patch.dex放到了dexElements的第一位,那麼當載入一個bug類時,發現在patch.dex中,則直接載入這個類,原來的bug類可能就被覆蓋了

看下PathClassLoader程式碼

public class PathClassLoader extends BaseDexClassLoader {

    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }

    public PathClassLoader(String dexPath, String libraryPath,
            ClassLoader parent) {
        super(dexPath, null, libraryPath, parent);
    }
} 

DexClassLoader程式碼

public class DexClassLoader extends BaseDexClassLoader {

    public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) {
        super(dexPath, new File(optimizedDirectory), libraryPath, parent);
    }
}

兩個ClassLoader就兩三行程式碼,只是呼叫了父類的建構函式.

public class BaseDexClassLoader extends ClassLoader {
    private final DexPathList pathList;

    public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String libraryPath, ClassLoader parent) {
        super(parent);
        this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
        Class c = pathList.findClass(name, suppressedExceptions);
        if (c == null) {
            ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \"" + name + "\" on path: " + pathList);
            for (Throwable t : suppressedExceptions) {
                cnfe.addSuppressed(t);
            }
            throw cnfe;
        }
        return c;
    }

在BaseDexClassLoader 建構函式中建立一個DexPathList類的例項,這個DexPathList的建構函式會建立一個dexElements 陣列

public DexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory) {
        ... 
        this.definingContext = definingContext;
        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
        //建立一個數組
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions);
        ... 
    }

然後BaseDexClassLoader 重寫了findClass方法,呼叫了pathList.findClass,跳到DexPathList類中.

/* package */final class DexPathList {
    ...
    public Class findClass(String name, List<Throwable> suppressed) {
            //遍歷該陣列
        for (Element element : dexElements) {
            //初始化DexFile
            DexFile dex = element.dexFile;

            if (dex != null) {
                //呼叫DexFile類的loadClassBinaryName方法返回Class例項
                Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
                if (clazz != null) {
                    return clazz;
                }
            }
        }       
        return null;
    }
    ...
} 

會遍歷這個陣列,然後初始化DexFile,如果DexFile不為空那麼呼叫DexFile類的loadClassBinaryName方法返回Class例項. 
歸納上面的話就是:ClassLoader會遍歷這個陣列,然後載入這個陣列中的dex檔案. 
而ClassLoader在載入到正確的類之後,就不會再去載入有Bug的那個類了,我們把這個正確的類放在Dex檔案中,讓這個Dex檔案排在dexElements陣列前面即可.

CLASS_ISPREVERIFIED問題

根據QQ空間談到的在虛擬機器啟動的時候,在verify選項被開啟的時候,如果static方法、private方法、建構函式等,其中的直接引用(第一層關係)到的類都在同一個dex檔案中,那麼該類就會被打上CLASS_ISPREVERIFIED標誌,且一旦類被打上CLASS_ISPREVERIFIED標誌其他dex就不能再去替換這個類。所以一定要想辦法去阻止類被打上CLASS_ISPREVERIFIED標誌。

為了阻止類被打上CLASS_ISPREVERIFIED標誌,QQ空間開發團隊提出了一個方法是先將一個預備好的hack.dex加入到dexElements的第一項,讓後面的dex的所有類都引用hack.dex其中的一個類,這樣原來的class1.dex、class2.dex、class3.dex中的所有類都引用了hack.dex的類,所以其中的都不會打上CLASS_ISPREVERIFIED標誌。

比如Qzon團隊的 安卓App熱補丁動態修復技術介紹  (這個一定要看!!! 他是熱修復元老級文章,也是重點抄襲物件)

動態載入class檔案,然後呼叫反射完成修復的原理:

Java程式在執行的時候,JVM通過類載入機制(ClassLoader)把class檔案載入到記憶體中,只有class檔案被載入記憶體,才能被其他class引用,使程式正確執行起來.

Java中的ClassLoader有三種.

1. Bootstrap ClassLoader 

由C++寫的,由JVM啟動.

啟動類載入器,負責載入java基礎類,對應的檔案是%JRE_HOME/lib/ 目錄下的rt.jar、resources.jar、charsets.jar和class等

2.Extension ClassLoader

Java類,繼承自URLClassLoader

擴充套件類載入器,對應的檔案是 %JRE_HOME/lib/ext 目錄下的jar和class等

3.App ClassLoader

Java類,繼承自URLClassLoader

系統類載入器,對應的檔案是應用程式classpath目錄下的所有jar和class等

這裡要注意一點:只有被同一個類載入器例項載入並且檔名相同的class檔案才被認為是同一個class.

下面來一個小例子:

因為系統的ClassLoader只會載入指定目錄下的class檔案,如果你想載入自己的class檔案,那麼就可以自定義一個ClassLoader.\

如何自定義ClassLoader

新建一個類繼承自java.lang.ClassLoader,重寫它的findClass方法。--將class位元組碼陣列轉換為Class類的例項---呼叫loadClass方法即可

我先建一個叫Log的類,很簡單,只有一句列印

  1. public class Log {  
  2.    public static void main(String[] args) {  
  3.         System.out.println("呼叫成功");  
  4.    }  
  5. }    

  6. 把這個java檔案放到D盤根目錄,然後開啟cmd,用javac命令把java檔案轉化為class檔案

  1. 然後我新建一個MyClassLoader繼承自ClassLoader
  2. public class MyClassLoader extends ClassLoader {  
  3.    @Override  
  4.    protected Class<?> findClass(String name) throws ClassNotFoundException {  
  5.        Class log = null;  
  6.        // 獲取該class檔案位元組碼陣列  
  7.        byte[] classData = getData();  
  8.        if (classData != null) {  
  9.            // 將class的位元組碼陣列轉換成Class類的例項  
  10.            log = defineClass(name, classData, 0, classData.length);  
  11.        }  
  12.        return log;  
  13.    }  
  14.    private byte[] getData() {  
  15.        //指定路徑  
  16.        String path = "D:/Log.class";  
  17.        File file = new File(path);  
  18.        FileInputStream in = null;  
  19.        ByteArrayOutputStream out = null;  
  20.        try {  
  21.            in = new FileInputStream(file);  
  22.            out = new ByteArrayOutputStream();  
  23.            byte[] buffer = new byte[1024];  
  24.            int size = 0;  
  25.            while ((size = in.read(buffer)) != -1) {  
  26.                out.write(buffer, 0, size);  
  27.            }  
  28.        } catch (IOException e) {  
  29.            e.printStackTrace();  
  30.        } finally {  
  31.            try {  
  32.                in.close();  
  33.            } catch (IOException e) {  
  34.                e.printStackTrace();  
  35.            }  
  36. 相關推薦

    修復系列之一----Android 修復原理方案比較

    熱修復說白了就是”即時無感打補丁”,比如你們公司上線一個app,使用者反應有重大bug,需要緊急修復。2015年以來,Android開發領域裡對熱修復技術的討論和分享越來越多,同時也出現了一些不同的解決方案.如果按照通常做法,那就是程式猿加班搞定bug,然後測試,重新打包

    Android 修復原理方案比較

    熱修復說白了就是”即時無感打補丁”,比如你們公司上線一個app,使用者反應有重大bug,需要緊急修復。2015年以來,Android開發領域裡對熱修復技術的討論和分享越來越多,同時也出現了一些不同的解決方案.如果按照通常做法,那就是程式猿加班搞定bug,然後測試,重新打包併

    Https系列之一:https的簡單介紹SSL證書的生成

    secret 兩種 人工 bin www out ext 隱私 發現 一:本文的主要內容介紹 https的介紹 SSL證書的介紹 自簽名SSL證書介紹及生成方法 CA證書介紹及申請 二:https的簡單介紹 HTTPS(全稱:Hyper Text Transfer P

    Android XListView實現原理講解分析

    就是 指定 不同 true -h -name 修改 一個 部分 XListview是一個非常受歡迎的下拉刷新控件,但是已經停止維護了。之前寫過一篇XListview的使用介紹,用起來非常簡單,這兩天放假無聊,研究了下XListview的實現原理,學到了很多,今天分享給大家。

    Python 系列之一基礎知識(一文章掌握基礎知識)

    概述近期在學習機器學習,機器學習中一般都推薦使用python 語言。使用python 語言,在資料處理方面相對於java 等其他語言來說,更加方便易容,python 可以引入第三方的庫,加以資料處理,比如處理矩陣等高等數學以及資料結構,還十分方便的進行程式資料的展示。本文中,

    Android修復技術原理詳解(最新最全版本)

    總結 核心 桌面圖標 實時 開源 穩定性 安卓 定義 check 本文框架 什麽是熱修復? 熱修復框架分類 技術原理及特點 Tinker框架解析 各框架對比圖 總結 ??通過閱讀本文,你會對熱修復技術有更深的認知,本文會列出各類框架的優缺點以及技術原理,文章末尾簡單描述

    Android修復技術原理分析

    2015年以來,Android開發領域裡對熱修復技術的討論和分享越來越多,同時也出現了一些不同的解決方案,如QQ空間補丁方案、阿里AndFix以 及微信Tinker,它們在原理各有不同,適用場景各異,到底採用哪種方案,是開發者比較頭疼的問題。本文希望通過介紹QQ空間補丁、Tinker以及基於AndF

    Android修復原理簡述

    本文為《2018夯實基礎》系列之熱修復原理簡述 作者:Bob 一、背景 ① 為什麼會出現熱修復技術? 大家都是開發,所以應該都知道有一個東西我們永遠也避免不了。不錯,**Bug!**我們在開發階段碰到bug那還好,直接解決就是了,大不了讓測試多測一輪。可是,如果

    深入探索Android修復技術原理讀書筆記——第一章:修復技術介紹

    第一章 熱修復技術介紹 1.1 什麼是熱修復 傳統開發流程: 重寫釋出版本代價太高 使用者下載安裝成本太高 bug修復不及時,使用者體驗差 有許多開發者找到了合適的解決辦法,比如: 經常變更的業務用H5獨立出來,但是增加

    《深入探索Android修復原理》程式碼修復總結

    阿里巴巴對熱修復技術的發展路線: 1、基於Xposed而來的Dalvik下java method hook技術-Dexposed框架,僅限於Dalvik虛擬機器 2、相容到Art虛擬機器的Andfix,同樣是基於底層的結構替換方案 3、進而發展就是h

    Android修復原理(HotFix)初涉

    寫在最前的話,一直聽說熱修復,不錯,最近修復風靡,不明白原理都不行,明白原理了不會用也不行,故打算拿出一些時間去深入瞭解一番 翻閱眾多資料 在此之前先感謝前人的資料提供, 好了 大家和我一起學習吧; * 首先明白幾個類的載入器:classLoader—

    Android修復(二):以DexClassLoader類載入原理編寫demo實現類替換修復

    上一篇文章簡易總結了熱修復實現的幾大原理,並詳細介紹了Android中的類載入機制及原始碼探索,Android的類載入機制涉及到ClassLoader、DexClassLoader 、PathClassLoader 、BaseDexClassLoader 、De

    android修復原理總結

    背景 當app釋出之後如果出現了緊急的線上bug,整個公司都會為此忙的焦頭爛額,現公司如果線上出現嚴重的P1級bug,甚至大半夜整個專案組都得來緊急修復上線,而bug的原因可能僅僅是傳錯了引數,或者寫錯一行程式碼,而且修復後的app又得重新上架,直到使用者更新

    Android 修復原理,DVM或ART與JVM的介紹ClassLoad雙親委派模型理解

    導語 熱修復說白了就是”打補丁”,通過事先設定的介面從網上下載無Bug的程式碼來替換有Bug的程式碼。這樣就省事多了,使用者體驗也好。這樣帶來的優勢就是成本低、效率高。熱修復的特點:無需重新發版,實時高效熱修復;使用者無感知修復,無需下載新的應用,代價小;修復成功率

    《深入探索Android修復技術原理》安卓修復原理寶典出爐,阿里技術牛聯袂推薦

    繼《阿里巴巴Java開發手冊》後,阿里為開發者帶來了第二份重磅大禮:業界首部安卓熱修復原理書籍——,該書為阿里巴巴手淘技術團隊撰寫,現已免費開放下載。  熱修復技術,可以看做是Android平臺發展成熟至一定階段的必然產物。它是一種維護流量、對使用者打攪極小的技術

    Android修復原理

    基礎 ssl 不同的 都是 優先 -o 除了 自然 思路 一. AndFix AndFix的原理就是方法的替換,把有bug的方法替換成補丁文件中的方法。註:在Native層使用指針替換的方式替換bug方法,已達到修復bug的目的。 AndFix采用native hook的方

    Android修復技術

    android新技術更新版本一直以來是移動端的一大痛點,各大公司也推出了相應的解決方案。1)AndFix(阿裏巴巴):兼容性不太好,親試過,上線反饋崩潰問題特別嚴重。2)Tinker(微信):集成起來是相當的麻煩 看完http://blog.csdn.net/u010983881/article/detai

    全面了解Android修復技術

    服務 補丁 按順序 體積 及其 試用 sta x文件 犯錯 WeTest 導讀 本文探討了Android熱修復技術的發展脈絡,現狀及其未來。 熱修復技術概述 熱修復技術在近年來飛速發展,尤其是在InstantRun方案推出之後,各種熱修復技術競相湧現。國內大部分成熟的主流

    Android 修復的相關總結(主要是阿裏百川的)

    else if aes stringbu tag initial att ide 新手 append 1.主流的熱修復是 QQ 、微信和阿裏百川 2.我建議使用阿裏百川的原因第一:團隊在釘釘有專門的客服 二、對於新手來說非常方便 3.操作步驟:阿裏百川的api文檔很詳細

    Android 補丁動態修復框架小結

    clas detail tle androi bsp blog .net details class Android 熱補丁動態修復框架小結Android 熱補丁動態修復框架小結