1. android 熱部署是什麼意思
在 Java 開發領域,熱部署一直是一個難以解決的問題,目前的 Java 虛擬機只能實現方法體的修改熱部署,對於整個類的結構修改,仍然需要重啟虛擬機,對類重新載入才能完成更新操作。對於某些大型的應用來說,每次的重啟都需要花費大量的時間成本。雖然 osgi 架構的出現,讓模塊重啟成為可能,但是如果模塊之間有調用關系的話,這樣的操作依然會讓應用出現短暫的功能性休克。本文將探索如何在不破壞 Java 虛擬機現有行為的前提下,實現某個單一類的熱部署,讓系統無需重啟就完成某個類的更新。
類載入的探索
首先談一下何為熱部署(hotswap),熱部署是在不重啟 Java 虛擬機的前提下,能自動偵測到 class 文件的變化,更新運行時 class 的行為。Java 類是通過 Java 虛擬機載入的,某個類的 class 文件在被 classloader 載入後,會生成對應的 Class 對象,之局差巧後就可以創建該類的實例。默認的虛擬機行為只會在啟動時載入類,如果後期有一個類需要更新的話,單純替換編譯的 class 文件,Java 虛擬機是不會更新正在運行的 class。如果要實現熱部署,最根本的方式是修改虛擬機的源代碼,改變 classloader 的載入行為,使虛擬機能監聽 class 文件的更新,重新載入 class 文件,這樣的行為破壞性很大,為後續的 JVM 升級埋下了一個大坑。
另一種友好的方法是創建自己的 classloader 來載入需要監聽的 class,這樣就能控制類載入的時機,從而實現熱部署。本文將具體探索如何實現這個方案。首先需要了解一下 Java 虛擬機現有的載入機制。目前的載入機制,稱為雙親委派,系統在使用一個 classloader 來載入類時,會先詢問當前 classloader 的父類是否有能力載入,如果父類無法實現載入操作,才會將任務下放到該 classloader 來載入。這種自上而下的載入方式的好處是,讓每個 classloader 執行自己的載入任務,不會重復載入類。但是這種方式卻使載入順序非常難改變,讓自定義 classloader 搶先載入需要監聽改變的類成為了一個難題。
不過我們可以換一個思路,雖然無法搶先載入該類,但是仍然可以用自定義 classloader 創建一個功能相同的類,讓每次實例化的對象都指向這個新的類。當這個類的 class 文件發生改變的時候,再次創建一個更新的類,之後如果系統再次發出實例化請求,創建的對象講指向這個全新的類。
下面來簡單列舉一下需要做的工作。
創建自定義的 classloader,載入需要監聽改變的類,在 class 文件發生改變的時候,重新載入該類。
改變創建對象的行為,使他們在創建時使用自定義 classloader 載入的 class。
自定義載入器的實現
自定義載入器仍然慶態需要執行類載入的功能。這里卻存在一個問題,同一個類載入器無法同時載入兩個相同名稱的類,由於不論類的結構如何發生變化,生成的類名不會變,而 classloader 只能在虛擬機停止前銷毀已經載入的類,這樣 classloader 就無法載入更新後的類了。這里有一個小技巧,讓每次載入的類都保存成一個帶有版本信息的 class,比如載入 Test.class 時,保存在內存中的類是 Test_v1.class,當類發生改變時,重新載入的類名是 Test_v2.class。但是真正執行載入 class 文件創建 class 的 defineClass 方法是一個 native 的方法,修改起來又變得很困難。所以面前還剩一條路,那就是直接修改編譯生成的 class 文件。
利用 ASM 修改 class 文件
可以修改位元組碼的框架有很多,比如 ASM,CGLIB。本文使用的是 ASM。先來介紹一下 class 文件的結構,class 文件包含了以下幾類信息,一個是類的基本信息,包含了訪問許可權信息,類名信息,父類信息,介面信息。第二個是類的變數信息。第三個是方法的信息。桐鍵ASM 會先載入一個 class 文件,然後嚴格順序讀取類的各項信息,用戶可以按照自己的意願定義增強組件修改這些信息,最後輸出成一個新的 class。
首先看一下如何利用 ASM 修改類信息。
清單 1. 利用 ASM 修改位元組碼
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassReader cr = null;
String enhancedClassName = classSource.getEnhancedName();
try {
cr = new ClassReader(new FileInputStream(
classSource.getFile()));
} catch (IOException e) {
e.printStackTrace();
return null;
}
ClassVisitor cv = new EnhancedModifier(cw,
className.replace(".", "/"),
enhancedClassName.replace(".", "/"));
cr.accept(cv, 0);
2. 求問大神現在做android的hotfix用哪個框架比較好
一.基礎知識1.阿里的熱更新框架已經開源了。但已經很久沒有更新過新版本了。當前的版本只支持到了Android4.4。由於5.0起新的ART虛擬機、更嚴格的SELinux策略以及對64位的支持之類的事,使得Xposed都在開發上做了很多調整。我不知道Dexposed現在是否支持,但至少阿里沒有開源。2.在本地動態執行遠端下發的代碼是極度危險的行為。利用此方法執行非法代碼等或用於繞過GooglePlay等市場的審查是違反相關協議的,也是對用戶極度不負責任的行為。3.在一些訪問非常密集的地方使用熱更新可能會對效率產生相對比較大的影響,應該避免使用.4.我們可以對Java的ScriptEngine進行一些封裝成為一個HotPatch類使得它更適合做熱更新的工作。5.首先,檢查熱更新補丁的管道一定要建立在https上,因為下發代碼是極其危險的,如果被劫持,後果是無法想像的。其次,請求時最好自動帶上Android版本、手機型號、地區、版本號等信息,以方便更精確地下發,千萬不能下發錯。6.Java在運行時載入對應的類是通過ClassLoader來實現的,ClassLoader本身是一個抽象來,Android中使用PathClassLoader類作為Android的默認的類載入器7.我們的如果想做hotpatch,一定要保證我們的hotpacthdex文件出現在dexElements列表的前面。二.常用的熱更新技術框架:基於 空間的HotFix→→要使用到androiddex分包方案→拆分dex的項目的話,可以參考一下谷歌的multidex方案實現.大眾點評的NuWa←項目補丁自動化做的很完整alibaba/AndFix阿里巴巴的DexPoseddalvik_patch實現multidex使用React-Native實現app熱部署的一次實踐alibaba/AndFix三、常用的熱更新技術框架比較AdvantagedisadavantageNuWa1,可以新增類和欄位,2,兼容到6.0系統1,基本原理是classloader,類載入器2,不能修改資源文件,如圖片布局等(可通過動態布局實現)AndFix1,支持Android2.3到6.0版本2,支持arm與x86系統架構3,支持dalvik和ART的runtime4,不需要重啟App即可應用補丁1,不能新增類和欄位,2,不能修改資源文件,3,不能修改manifest文件4,不能新增成員變數5,不能使用加固後的apk製作pacth文件四、github地址網路的同學的實現HotFix點評的同學的實現Nuwa阿里的同學的實現AndFix另:AndFix對static的支持不太好,下面是試驗的Demo:添加了一個靜態的欄位addString:通過AndFix來製作patch會直接報錯: