業界首個非侵入式熱修複方案Sophix重磅推出,顛覆移動端傳統更新流程!
橫空出世
阿裏巴巴對Android熱修複技術已經進行了長達多年的探索。
最開始,是手淘基於Xposed進行了改進,產生了針對Android Dalvik虛擬機運行時的Java Method Hook技術,Dexposed。但這個方案由於對底層Dalvik結構過於依賴,最終無法繼續兼容Android5.0以後ART虛擬機,因此作罷。
後來支付寶提出了新的熱修複方案Andfix。Andfix同樣是一種底層結構替換的方案,也達到了運行時生效即時修複的效果,並且重要的是,做到了Dalvik和ART環境的全版本兼容。阿裏百川結合手淘在實際工程中使用Andfix的經驗,對相關業務邏輯解耦後,推出了阿裏百川Hotfix方案,並得到了良好的反響。
此時的百川Hotfix已經是一個很不錯的產品了,對於基本的代碼修複需求都可以解決,安全性和易用性都做的比較好。然而,它所依賴的基石,Andfix本身,是有局限性的。且不說其底層固定結構的替換方案穩定性不好,其使用範圍也存在著諸多限製,雖然可以通過改造代碼繞過限製來達到相同的修複目的,但這種方式既不優雅也不方便。而更大的問題是,Andfix隻提供了代碼層麵的修複,對於資源和so的修複都還未能實現。
再看一下同期的其他熱修複方案,此時的熱修複技術可謂是百花齊放,微信的Tinker、QQ空間的Nuwa、餓了麼的Amigo、美團的Robust等等,各個熱修複方案爭相發布,都聲稱自己可以做到全方位全功能的熱修複。不過他們各自有自身的局限性,或者不夠穩定,或者補丁過大,或者效率低下,或者使用起來過於繁瑣,大部分技術上看起來似乎可行,但實際體驗並不好。而在我們看來,有很多技術細節能夠做得更加完美。
終於在2017年6月11日,手淘技術團隊聯合阿裏雲正式發布了新一代Android移動熱修複方案——Sophix。
Sophix的橫空出世,將會打破各家熱修複技術紛爭的局麵。我們可以滿懷信心地說,在Android熱修複的三大領域:代碼修複、資源修複、so修複方麵,以及方案的安全性和易用性方麵,Sophix都做到了業界領先。
設計理念
Sophix的核心設計理念,就是非侵入性。
我們的打包過程不會侵入到apk的build流程中。我們所需要的,隻有已經生成完畢的新舊apk,而至於apk是如何生成的——是Android Studio打包出來的、還是Eclipse打包出來的、或者是自定義的打包流程,我們一律不關心。在生成補丁的過程中間既不會改變任何打包組件,也不插入任何AOP代碼,我們極力做到了——不添加任何超出開發者預期的代碼,以避免多餘的熱修複代碼給開發者帶來困擾。
在Sophix中,唯一需要的就是初始化和請求補丁兩行代碼,甚至連入口Application類我們都不做任何修改,這樣就給了開發者最大的透明度和自由度。我們甚至重新開發了打包工具,使得補丁工具操作圖形界麵化,這種所見即所得的補丁生成方式也是阿裏熱修複獨家的。因此,Sophix的接入成本也是目前市麵上所有方案裏最低的。
這種非侵入式熱更新理念,是我們在設計過程中從用戶使用角度進行了深入思考而提煉出的核心思想。
這裏的用戶,指的自然是廣大的開發者。對於開發者而言,熱修複應該是一個與業務無關的SDK組件,在整個開發過程中感知不到它的存在。最理想的情況,就是開發者拿過來兩個apk,一個是已經安裝在手機上的apk,另一個是將要發布出去的apk。我們直接通過工具,就可以根據這兩個apk生成補丁,然後把這個補丁下發給已經安裝的舊app上,就可以直接加載,使舊app重生為新的app。而這個加載了補丁包新app,在功能和使用上,將會和直接安裝新apk別無二致。
至於Sophix這個名字,是來源於Sophic(明智的)+ FIX,一個更明智的熱修複方案。
詳細比較
下麵的這張表格,從幾個熱修複最重要的維度,把Sophix和另外兩個主要商業化熱修複方案進行了比較。
方案對比 | Sophix | Tinker | Amigo |
---|---|---|---|
DEX修複 | |||
資源更新 | |||
SO庫更新 | |||
性能損耗 | |||
四大組件 | |||
生成補丁 | |||
補丁大小 | |||
接入成本 | |||
Android版本 | |||
安全機製 | |||
服務端支持 |
可以看到,Sophix在各個指標上全麵占優。而其中唯一不支持的地方就是四大組件的修複,這是因為如果要修複四大組件,必須在AndroidManifest裏麵預先插入代理組件,並且盡可能聲明所有權限,而這麼做就會給原先的app添加很多臃腫的代碼,對app運行流程的侵入性很強,所以,本著對開發者透明與代碼極簡的原則,我們沒有做這種多餘的處理。
直接看表格的話,其中有些技術細節可能還看不太明朗,那麼接下來,我將從各個角度,深度解讀Sophix的技術優勢以及它與同類技術的差別。
技術分析
Sophix的誕生,起初是對原先的阿裏百川Hotfix 1.X版本進行升級衍進。
原先百川Hotfix服務端的整套請求控製流程,以及安全檢查這部分,是與熱修複功能相對分離的,因此我們依舊保留了這部分的邏輯。
而原本的熱修複方案,主要限製在於Andfix本身,我們最開始也是從突破原先修複限製入手,希望能夠基於原先的Andfix代碼做一些必要的改進。然而最終發現,Andfix自身限製幾乎是無法繞過的,在運行時對原有類的結構是已經固化在內存中的,它的一些動態屬性和很難進行擴展。並且由於Android係統的碎片化,廠商的虛擬機底層結構都不是確定的,因此直接基於原先機製進行擴展的風險很大。
所以我們繞開了具體的技術實現細節,直接從修複的原理入手,對原先的代碼修複技術進行深挖和改良。
回顧為期九個多月的探索與開發,這其中無處不體現著我們對易用性和優雅性的極致追求,在技術先進性與易用性上我們達到了完美的平衡。所以,當我們再回頭看目前市麵上的其他熱修複技術,真的有一種“曾經滄海難為水”的感覺。
代碼修複
代碼修複有兩大主要方案,一種是阿裏係的底層替換方案,另一種是騰訊係的類加載方案。
這兩類方案各有優劣:
底層替換方案限製頗多,但時效性最好,加載輕快,立即見效。
類加載方案時效性差,需要重新冷啟動才能見效,但修複範圍廣,限製少。
底層替換方案。
底層替換方案是在已經加載了的類中直接替換掉原有方法,是在原來類的基礎上進行修改的。因而無法實現對與原有類進行方法和字段的增減,因為這樣將破壞原有類的結構。
一旦補丁類中出現了方法的增加和減少,就會導致這個類以及整個Dex的方法數的變化。方法數的變化伴隨著方法索引的變化,這樣在訪問方法時就無法正常地索引到正確的方法了。如果字段發生了增加和減少,和方法變化的情況一樣,所有字段的索引都會發生變化。並且更嚴重的問題是,如果在程序運行中間某個類突然增加了一個字段,那麼對於原先已經產生的這個類的實例,它們還是原來的結構,這是無法改變的。而新方法使用到這些老的實例對象時,訪問新增字段就會產生不可預期的結果。
這是這類方案的固有限製,而底層替換方案最為人詬病的地方,在於底層替換的不穩定性。
傳統的底層替換方式,不論是Dexposed、Andfix或者其他安全界的Hook方案,都是直接依賴修改虛擬機方法實體的具體字段。例如,改Dalvik方法的jni函數指針、改類或方法的訪問權限等等。這樣就帶來一個很嚴重的問題,由於Android是開源的,各個手機廠商都可以對代碼進行改造,而Andfix裏ArtMethod的結構是根據公開的Android源碼中的結構寫死的。如果某個廠商對這個ArtMethod結構體進行了修改,就和原先開源代碼裏的結構不一致,那麼在這個修改過了的設備上,通用性的替換機製就會出問題。這便是不穩定的根源。
而我們也對代碼的底層替換原理重新進行了深入思考,從克服其限製和兼容性入手,以一種更加優雅的替換思路,實現了即時生效的代碼熱修複。我們實現的是一種無視底層具體結構的替換方式,
這麼一來,我們不僅解決了兼容性問題,並且由於忽略了底層ArtMethod結構的差異,對於所有的Android版本都不再需要區分,代碼量大大減少。即使以後的Android版本不斷修改ArtMethod的成員,隻要保證ArtMethod數組仍是以線性結構排列,就能直接適用於將來的Android 8.0、9.0等新版本,無需再針對新的係統版本進行適配了。事實也證明確實如此,當我們拿到Google剛發不久的Android O(8.0)開發者預覽版的係統時,hotfix demo直接就能順利地加載補丁跑起來了,我們並沒有做任何適配工作,魯棒性極好。
具體技術細節,可以看這篇文章:Android熱修複升級探索——追尋極致的代碼熱替換
類加載方案
類加載方案的原理是在app重新啟動後讓Classloader去加載新的類。因為在app運行到一半的時候,所有需要發生變更的類已經被加載過了,在Android上是無法對一個類進行卸載的。如果不重啟,原來的類還在虛擬機中,就無法加載新類。因此,隻有在下次重啟的時候,在還沒走到業務邏輯之前搶先加載補丁中的新類,這樣後續訪問這個類時,就會Resolve為新類。從而達到熱修複的目的。
再來看看騰訊係三大類加載方案的實現原理。QQ空間方案會侵入打包流程,並且為了hack添加一些無用的信息,實現起來很不優雅。而QFix的方案,需要獲取底層虛擬機的函數,不夠穩定可靠,並且有個比較大的問題是無法新增public函數。
微信的Tinker方案是完整的全量dex加載,並且可謂是將補丁合成做到了極致,然而我們發現,精密的武器並非適用於所有戰場。Tinker的合成方案,是從dex的方法和指令維度進行全量合成,整個過程都是自己研發的。雖然可以很大地節省空間,但由於對dex內容的比較粒度過細,實現較為複雜,性能消耗比較嚴重。實際上,dex的大小占整個apk的比例是比較低的,一個app裏麵的dex文件大小並不是主要部分,而占空間大的主要還是資源文件。因此,Tinker方案的時空代價轉換的性價比不高。
其實,dex比較的最佳粒度,應該是在類的維度。它既不像方法和指令維度那樣的細微,也不像bsbiff比較那般的粗糙。在類的維度,可以達到時間和空間平衡的最佳效果。基於這個準則,我們另辟蹊徑,實現了一種完全不同的全量dex替換方案。
我們采用的也是全量合成dex的技術,這個技術是從手淘插件化框架Atlas汲取的。我們會直接利用Android原先的類查找和合成機製,快速合成新的全量dex。這麼一來,我們既不需要處理合成時方法數超過的情況,對於dex的結構也不用進行破壞性重構。
從圖中可以看到,我們重新編排了包中dex的順序。這樣,在虛擬機查找類的時候,會優先找到classes.dex中的類,然後才是classes2.dex、classes3.dex,也可以看做是dex文件級別的類插樁方案。這個方式十分巧妙,它對舊包與補丁包中classes.dex的順序進行了打破與重組,最終使得係統可以自然地識別到這個順序,以實現類覆蓋的目的。這將會大大減少合成補丁的開銷。
雙劍合璧
既然底層替換方案和類加載方案各有其優點,把他們聯合起來不是最好的選擇嗎?Sophix的代碼修複體係正是同時涵蓋了這兩種方案。兩種方案的結合,可以實現優勢互補,完全兼顧的作用,可以靈活地根據實際情況自動切換。
這兩種方案我們都進行了重大的改進,並且從補丁生成到應用的各個環節都進行了研究,使得二者能很好地整合在一起。在補丁生成階段,補丁工具會根據實際代碼變動情況進行自動選擇,針對小修改,在底層替換方案限製範圍內的,就直接采用底層替換修複嗎,這樣可以做到代碼修複即時生效。而對於代碼修改超出底層替換限製的,會使用類加載替換,這樣雖然及時性沒那麼好,但總歸可以達到熱修複的目的。
另外,運行時階段,Sophix還會再判斷所運行的機型是否支持熱修複,這樣即使補丁支持熱修複,但由於機型底層虛擬機構造不支持,還是會走類加載修複,從而達到最好的兼容性。
資源修複
目前市麵上的很多資源熱修複方案基本上都是參考了Instant Run的實現。實際上,Instant Run的推出正是推動這次熱修複浪潮的主因,各家熱修複方案,在代碼、資源等方麵的實現,很大程度上地參考了Instant Run的代碼,而資源修複方案正是被拿來用到最多的地方。
簡要說來,Instant Run中的資源熱修複分為兩步:
構造一個新的AssetManager,並通過反射調用addAssetPath,把這個完整的新資源包加入到AssetManager中。這樣就得到了一個含有所有新資源的AssetManager。
找到所有之前引用到原有AssetManager的地方,通過反射,把引用處替換為AssetManager。
我們發現,其實大量代碼都是在處理兼容性問題和找到所有AssetManager的引用處,真正的替換的邏輯其實很簡單。
我們的方案沒有直接使用Instant Run的技術,而是另辟蹊徑,構造了一個package id為0x66的資源包,這個包裏隻包含改變了的資源項,然後直接在原有AssetManager中addAssetPath這個包就可以了。由於補丁包的package id為0x66,不與目前已經加載的0x7f衝突,因此直接加入到已有的AssetManager中就可以直接使用了。補丁包裏麵的資源,隻包含原有包裏麵沒有而新的包裏麵有的新增資源,以及原有內容發生了改變的資源。並且,我們采用了更加優雅的替換方式,直接在原有的AssetManager對象上進行析構和重構,這樣所有原先對AssetManager對象的引用是沒有發生改變的,所以就不需要像Instant Run那樣進行繁瑣的修改了。
可以說,我們的資源修複方案,優越性超過了Google官方的Instant Run方案。整個資源替換的方案優勢在於:
不修改AssetManager的引用處,替換更快更完全。(對比Instanat Run以及所有copycat的實現)
不必下發完整包,補丁包中隻包含有變動的資源。(對比Instanat Run、Amigo等方式的實現)
不需要在運行時合成完整包。不占用運行時計算和內存資源。(對比Tinker的實現)
所以,我們不要被所謂的“官方實現”束縛住手腳,其實Instant Run的開發團隊和Android framework的開發團隊並不是同一個團隊,他們對於Android係統機製的理解未必十分深入。隻要你認真研讀係統代碼,實現一個比官方更好的方案絕非難事。所以我想說的是,要想實現技術方案的突破,首先就需要破除所謂“權威”的觀念。
資源修複的更多技術細節,可通過這篇文章一探究竟:Android熱修複升級探索——資源更新之新思路
so庫修複
so庫的修複本質上是對native方法的修複和替換。
我們知道JNI編程中,native方法可以通過動態注冊和靜態注冊兩種方式進行。動態注冊的native方法必須實現JNI_OnLoad
方法,同時實現一個JNINativeMethod[]
數組,靜態注冊的native方法必須是Java+類完整路徑+方法名
的格式。
動態注冊的native方法映射通過加載so庫過程中調用JNI_OnLoad方法調用完成,靜態注冊的native方法映射是在該native方法第一次執行的時候才完成映射,當然前提是該so庫已經load過。
我們采用的是類似類修複反射注入方式。把補丁so庫的路徑插入到nativeLibraryDirectories數組的最前麵,就能夠達到加載so庫的時候是補丁so庫,而不是原來so庫的目錄,從而達到修複的目的。
采用這種方案,完全由Sophix在啟動期間反射注入patch中的so庫。對開發者依然是透明的。不用像某些其他方案需要手動替換係統的System.load來實現替換目的。
未來展望
熱修複的必要性
熱修複是一個與業務完全無關的模塊,開發者如果要自己實現一套可靠的熱修複框架,將花費大量時間和精力。雖然市麵上已經有很多開源的熱修複實現,然而其中的很多坑,往往要踩過才知道,等你把這些坑一一踩過之後,可能大量的用戶已經對你失去信心。所以,依靠一個穩定可靠、而且簡單實用的商業版本,反而能使各方麵的成本降到最低。並且,熱修複並不是簡單的客戶端SDK,它還包含了安全機製和服務端的控製邏輯,這整條鏈路也不是短時間內可以快速完成的。
還是那句老話,專業是事交給專業的人去做。開發者應該把更多時間精力放到自己的核心業務之中。
Sophix提供了一套更加完美的客戶端服務端一體的熱更新方案。做到了圖形界麵一鍵打包、加密傳輸、簽名校驗和服務端控製發布與灰度功能,讓你用最少的時間實現最強大可靠的全方位熱更新。並且在代碼修複、資源修複、SO庫修複方麵,都做到了業界最佳。
對Android的生態的影響
很多人會把熱修複技術跟其他國內廠商的“黑科技”混為一談。有人說,你們國內開發者就是瞎搞,就不能給我們Android用戶一個更加純淨的環境嗎?
這裏我需要澄清一下。熱修複技術不同於其他國內的Android“黑科技”。就比如,國內Android進程保活,是讓app持續駐留在後台避免被係統殺死,這既耗費手機電量又占內存,浪費了很多手機資源。再比如,app自行定製的推送服務,無節操地對用戶進行信息轟炸。還有更過分的全家桶,一個app同時拉起一票app,並且長期占著內存,使得手機卡頓不堪。總歸,這些技術都是為了app廠商的利益而損害手機使用者的實際體驗。
而熱修複技術是完全不同的,它達到的是一個手機用戶和開發者雙贏的目的。不僅廠商可以快速迭代更新app,使得功能能最快上線。並且由於熱更新過程是毫無感知的,手機用戶也減少了繁瑣的更新步驟,節省了大量等待更新的時間。這實際上是改善了Android的生態環境。隻是這其中最重要的,是要保證熱修複功能的穩定性。而Sophix的穩定性,是經過了無數開發者檢驗的,並且還有手淘多年深厚的技術沉澱作為保障。
Android與iOS熱修複的不同
前段時間,蘋果封殺了iOS的熱修複功能,很多人就因此不看好熱修複技術了。這裏我想說的是,蘋果的政策並不能證明他有多先進,相反,作為獨裁者,蘋果做過很多不得人心的事,就比如前段時間封殺微信的文章打賞功能。熱修複功能被禁止,會使得很多app不得不靠直接發版進行更新,這樣一旦新版本出了問題,整個更新迭代過程變得十分漫長。並且一些試驗性功能無法進行灰度,這就使得一個重要功能的更新將直接全量發版,如果功能不夠穩定,波及範圍就變得非常廣。而且,用戶需要重新下載整個app,不僅流程漫長,原本不到1MB的補丁就能解決的事,現在不得不下載幾十MB的完整包才能更新。
再看回Android的情況,Android熱修複和iOS是有極大不同的。主要有兩個方麵:
穀歌和蘋果在中國的地位不同
Android和IOS的開放性不同
穀歌在中國沒有像蘋果那樣的控製力,即使它想要封殺也不可能,國內是有各個安卓應用市場的,沒有統一的app安裝渠道。另外,Android是開源的,各個廠商都可以做定製,想統一各家的安裝渠道幾乎是不可能的。
未來,無限可能!
我們對於未來是很樂觀的,Android的熱修複領域不僅不會受到封殺,反而還有很大的發展空間。我們正在嚐試支持各大加固廠商,目前阿裏聚安全修複已經支持了Sophix,熱修複結合安全加固,將會使得app的穩定性和安全性更加堅不可摧。甚至後續還可以與係統廠商合作,對係統app乃至係統組件進行修複,這樣就可以避免頻繁OTA升級。
因此,熱修複所能發揮是價值將是十分巨大的。熱修複還可以與其他領域進行碰撞,引發無限的可能性。在這裏,我們歡迎所有應用廠商以及ROM廠商與我們合作,共同使得Android的生態更加完善。
微信公眾號文章對應鏈接:https://mp.weixin.qq.com/s/5KjSPvUflbg0pVRIjtLiRA
最後更新:2017-06-15 11:31:48
上一篇:
RDS SQL Server 設置SSL加密連接
下一篇:
如何實現高容量大並發數據庫服務 | 數據庫分布式架構設計
排序算法
【端午小練】HDU1021-最簡單的計算機
RDS SQL Server - 專題分享 - 巧用執行計劃緩存之統計信息缺失警告
nginx站點配置之域名301重定向
[WCF-Discovery]如何利用”發現代理”實現可用服務的實時維護?
手把手,教你用MaxCompute+OpenSearch搭建分布式搜索引擎
PostgreSQL Oracle兼容性 - 計算字符長度與字節長度(char(?) 與varchar(?)空格如何計算長度)
5月15日雲棲精選夜讀:重要通知 | 比特幣勒索席卷全球,如何防範?
《時代》評選17年100位全球影響力人物,滴滴柳青、DeepMind創始人等科技大佬上榜
Linq中更新操作的思路