編輯:關於Android編程
繼插件化後,熱補丁技術在2015年開始爆發,目前已經是非常熱門的Android開發技術。其中比較著名的有淘寶的Dexposed、支付寶的AndFix以及QZone的超級熱補丁方案。微信對熱補丁技術的研究並不算早,大約開始於2015年6月。經過研究與嘗試現有的各個方案,我們發現它們都有著自身的一些局限性。微信最終采用不同於它們的技術方案,走出了自己的實踐演進之路。
另外一方面,技術應當只是熱補丁方案中的一環。隨著對熱補丁的多次嘗試與應用,微信建立起自身的流程規范,同時也不斷的嘗試拓展它的應用場景。通過本文,我希望大家不僅能夠全面的了解各項熱補丁技術的優缺點,同時也能對它的應用場景有著更加全面的認識。在此基礎上,大家或許能更容易的決定是否在自己的項目中使用熱補丁技術,以及應當如何使用它。
熱補丁:讓應用能夠在無需重新安裝的情況實現更新,幫助應用快速建立動態修復能力。
從上面的定義來看,熱補丁節省Android大量應用市場發布的時間。同時用戶也無需重新安裝,只要上線就能無感知的更新。看起來很美好,這是否可以意味我們可以盡量使用補丁來代替發布呢?事實上,熱補丁技術當前依然存在它的局限性,主要表現在以下幾點:
補丁只能針對單一客戶端版本,隨著版本差異變大補丁體積也會增大; 補丁不能支持所有的修改,例如AndroidManifest; 補丁無論對代碼還是資源的更新成功率都無法達到100%。既然補丁技術無法完全代替升級,那它適合使用在哪些場景呢?
熱補丁技術也可以理解為一個動態修改代碼與資源的通道,它適合於修改量較少的情況。以微信的多次發布為例,補丁大小均在300K以內,它相對於傳統的發布有著很大的優勢。
以Android用戶的升級習慣,即使是相對活躍的微信也需要10天以上的時間去覆蓋50%的用戶。使用補丁技術,我們能做到1天覆蓋70%以上。這也是基於補丁體積較小,可以直接使用移動網絡下載更新。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPtX90vLI57TLo6yyubahvLzK9bfHs6PKyrrPyrnTw9Tau9K2yL3Xts6ho9Tauf3IpaOsztLDx9Do0qrU2tX9yr23orK8x7Cxo9aky/nT0NHP1ti1xM7KzOK2vNLRvq21w7W90N64tKOs1eLNqLOj0OjSqs7Sw8e+rbn9yP20ztLUyc+1xLvStsi5/bPMo6y2+MfSzt63qL/sy9m1xNHp1qTV4tCpzsrM4tTazazSu8X608O7p7XE0N64tNCnufuho8D708PIyLK5tqG8vMr1o6zO0sPHv8nS1L/sy9m21M2s0rvF+tPDu6fR6dak0N64tNCnufujrNXitPO088v1tszBy87Sw8e1xLeisrzB97PMoaM8L3A+DQo8cD48aW1nIGFsdD0="" src="/uploadfile/Collfiles/20160705/20160705091658595.jpg" title="\" />
若發布版本出現問題或緊急漏洞,傳統方式需要單獨灰度驗證修改,然後重新發布新的版本。利用補丁技術,我們只需要先上線小部分用戶驗證修改的效果,最後再全量上線即可。但是此種發布對線上用戶影響較大, 我們需要謹慎而為。本著對用戶負責的態度,發布補丁等同於發布版本,它也應該嚴格執行完整的測試與上線流程。
總的來說,補丁技術可以降低開發成本,縮短開發周期,實現輕量而快速的升級。
一入Android深似海,Android開發的另外一個痛是機型的碎片化。我們也許都會遇到”本地不復現”,”日志查不出”,”聯系用戶不鳥你”的煩惱。所以補丁機制非常適合使用在遠端調試上。即我們需要具備只特定用戶發送補丁的能力,這對我們查找問題非常有幫助。
利用補丁技術,我們避免了騷擾用戶而默默的為用戶解決問題。當然這也需要非常嚴格的權限管理,以防惡意或隨意使用。
數據統計在微信中也占據著非常重要的位置,我們也非常希望將熱補丁與數據統計結合的更好。事實上,熱補丁無論在普通的數據統計還是ABTest都有著非常大的優勢。例如若我想對同一批用戶做兩種test, 傳統方式無法讓這批用戶去安裝兩個版本。使用補丁技術,我們可以方便的對同一批用戶不停的更換補丁。
在數據統計之路,如何與補丁技術結合的更好,更加精准的控制樣本人數與比例,這也是微信當前努力發展的一個方向。
事實上,Android官方也使用熱補丁技術實現Instant Run。它分為Hot Swap、Warm Swap與Cold Swap三種方式,大家可以參考英文介紹,也可以看參考文章中的翻譯稿。最新的Instant App應該也是采用類似的原理,但是Google Play是不允許下發代碼的,這個海外App需要注意一下。
在了解補丁技術可以與適合做什麼之後,我們回到技術本身。由於Dexposed無法支持全平台,並不適合應用到商業產品中。所以這裡我們只簡單介紹Andfix、QZone、微信幾套方案的實現,以及它們方案面臨著的問題,大家也可以參考資料中的各大熱補丁方案分析和比較一文。
AndFix采用native hook的方式,這套方案直接使用dalvik_replaceMethod
替換class中方法的實現。由於它並沒有整體替換class, 而field在class中的相對地址在class加載時已確定,所以AndFix無法支持新增或者刪除filed的情況(通過替換init
與clinit
只可以修改field的數值)。
也正因如此,Andfix可以支持的補丁場景相對有限,僅僅可以使用它來修復特定問題。結合之前的發布流程,我們更希望補丁對開發者是不感知的,即他不需要清楚這個修改是對補丁版本還是正式發布版本(事實上我們也是使用git分支管理+cherry-pick方式)。另一方面,使用native替換將會面臨比較復雜的兼容性問題。
相比其他方案,AndFix的最大優點在於立即生效。事實上,AndFix的實現與Instant Run的熱插拔有點類似,但是由於使用場景的限制,微信在最初期已排除使用這一方案。
QZone方案並沒有開源,但在github上的Nuwa采用了相同的方式。這個方案使用classloader的方式,能實現更加友好的類替換。而且這與我們加載Multidex的做法相似,能基本保證穩定性與兼容性。具體原理在這裡不再細說,大家可以參考這篇文章。
本方案為了解決unexpected DEX problem
異常而采用插樁的方式,從而規避問題的出現。事實上,Android系統的這些檢查規則是非常有意義的,這會導致QZone方案在Dalvik與Art都會產生一些問題。
若采用插樁導致所有類都非preverify,這導致verify與optimize操作會在加載類時觸發。這會有一定的性能損耗,微信分別采用插樁與不插樁兩種方式做過兩種測試,一是連續加載700個50行左右的類,一是統計微信整個啟動完成的耗時。
平均每個類verify+optimize(跟類的大小有關系)的耗時並不長,而且這個耗時每個類只有一次。但由於啟動時會加載大量的類,在這個情況影響還是比較大的。
Art; Art采用了新的方式,插樁對代碼的執行效率並沒有什麼影響。但是若補丁中的類出現修改類變量或者方法,可能會導致出現內存地址錯亂的問題。為了解決這個問題我們需要將修改了變量、方法以及接口的類的父類以及調用這個類的所有類都加入到補丁包中。這可能會帶來補丁包大小的急劇增加。這裡是因為在dex2oat時fast*
已經將類能確定的各個地址寫死。如果運行時補丁包的地址出現改變,原始類去調用時就會出現地址錯亂。這裡說的可能不夠詳細,事實上微信當時為了查清這兩個問題,也花費了一定的時間將Dalvik跟Art的流程基本搞透。若大家對這裡感興趣,後續在單獨的文章詳細論述。
總的來說,Qzone方案好處在於開發透明,簡單,這一套方案目前的應用成功率也是最高的,但在補丁包大小與性能損耗上有一定的局限性。特別是無論我們是否真正應用補丁,都會因為插樁導致對程序運行時的性能產生影響。微信對於性能要求較高,所以我們也沒有采用這套方案。
有沒有那麼一種方案,能做到開發透明,但是卻沒有QZone方案的缺陷呢?Instant Run的冷插拔與buck的exopackage或許能給我們靈感,它們的思想都是全量替換新的Dex。即我們完全使用了新的Dex,那樣既不出現Art地址錯亂的問題,在Dalvik也無須插樁。當然考慮到補丁包的體積,我們不能直接將新的Dex放在裡面。但我們可以將新舊兩個Dex的差異放到補丁包中,最簡單我們可以采用BsDiff算法。
簡單來說,在編譯時通過新舊兩個Dex生成差異path.dex。在運行時,將差異patch.dex重新跟原始安裝包的舊Dex還原為新的Dex。這個過程可能比較耗費時間與內存,所以我們是單獨放在一個後台進程:patch中。為了補丁包盡量的小,微信自研了DexDiff算法,它深度利用Dex的格式來減少差異的大小。它的粒度是Dex格式的每一項,可以充分利用原本Dex的信息,而BsDiff的粒度是文件,AndFix/QZone的粒度為class。
這塊後面我希望後面用單獨的文章來講述,這裡先做一個鋪墊,大致的效果如下圖。在最極端的情況,由於利用了原本dex的信息完全替換一個13M的Dex,我們的補丁大小也僅僅只有6.6M。
但是這套方案並非沒有缺點,它帶來的問題有兩個:
占用Rom體積;這邊大約是你修改Dex數量的1.5倍(dexopt與dex壓縮成jar)的大小。 一個額外的合成過程;雖然我們單獨放在一個進程上處理,但是合成時間的長短與內存消耗也會影響最終的成功率。微信的熱補丁方案叫做Tinker,也算緬懷一下Dota中的地精修補匠,希望能做到無限刷新。
限於篇幅,這裡對Dex、library以及資源的更多技術細節並沒有詳細的論述,這裡希望放在後面的單獨文章中。我們最後從整體比較一下這幾種方案:
若不care性能損耗與補丁包大小,QZone方案是最簡單且成功率最高的方案(沒有單獨的合成過程)。相對Tinker來說,它的占用Rom體積也更小。另一方面,QZone與Tinker的成功率大約相差3%左右。
事實上,一個完整的框架應該也是一個容易使用的框架。Tinker對補丁版本管理、進程管理、安全校驗等都有著很好的支持。同時我們也支持gradle與命名行兩種接入方式。希望在不久的將來,它可以很快的跟大家見面。
上一章節我們簡單比較了各個熱補丁的技術方案,它們解決了如何生成與加載補丁包的問題。但一個完善的熱補丁系統不應該僅限於此,它還需要包括以下幾個方面:
網絡通道;這裡要解決的問題是決定補丁以何種方式推送給哪部分的用戶。 上線與後台管理平台;這裡主要包括熱補丁的上線管理,歷史管理以及上報分析,報警監控等;網絡通道負責的將補丁包交付給用戶,這個包括特定用戶與全量用戶兩種情況。事實上,微信當前針對熱補丁有以下三種通道更新:
pull通道; 在登陸/24小時等時機,通過pull方式查詢後台是否有對應的補丁包更新,這也是我們最常用的方式; 指定版本的push通道; 針對版本的通道,在緊急情況下,我們可以在一個小時內向所有用戶下發補丁包更新。 指定特定用戶的push通道;對特定用戶或用戶組做遠程調試。事實上,對於大部分的應用來說,假設不實現push通道,CDN+pull通道實現起來還是較為容易。
上線與管理平台主要為了快速上線,管理歷史記錄,以及監控補丁的運行情況等。
事實上,微信發布熱補丁是非常慎重的。它整個發布流程與升級版本是保持一致的,也必須修改版本號、經過嚴格的完整測試流程等。我們也會通過灰度的方式上線,同時監控補丁版本的各個指標。這裡的為了完整的監控補丁的情況,我們做的工作有:
1分鐘粒度的每小時/每天的各版本累積用戶,及時監控補丁版本的人數與活躍; 3分鐘粒度的Crash統計,基准版本與補丁版本的Crash每小時/每天的兩個維度對照; 10分鐘粒度的補丁監控信息上報。應用成功率= 補丁版本人數/補丁發布前該版本人數
由於可能存在基准或補丁版本用戶安裝了其他版本,所以本統計結果應略為偏低,但它能現實的反應補丁的線上覆蓋情況。
使用Qzone方案,微信補丁在10天後的應用成功率大約在98.5%左右。使用Tinker大約只有95.5%左右,主要原因在於空間不足以及後台進程被殺。在這裡我們也在嘗試使用重試的方式以及降低合成的耗時與內存,從而提升成功率。
熱補丁技術發展的很快,Android推出的Instant App也令人期待。但是在國內,似乎我們還是指望自己更靠譜一點。每一個的應用的需求都不太一致,這裡大致講了一些微信的實踐經驗,希望對大家有幫助。
隨著微信部門內從“單APP”向“多APP”演進,微信也正在邁入開源化的開發實踐。我們希望將各個功能組件化,從而做可以到快速復制與應用。微信的熱補丁框架“Tinker”當前也在經歷從微信分離,又合入到微信的過程。希望在不久的將來,我們也可以將“Tinker”以及微信中一些其他的組件開源出去。
我們也希望可以找一個App作為內測,給我們提供寶貴的意見。若對微信的Tinker方案感興趣的用戶,可以單獨發消息或在文章末留言。注明姓名、所在公司以及負責的App,我們希望挑選部分產品作為內測。
自定義dialog肯定是用的很多了但是感覺每次做都是很亂 單純完成任務而已,現在封裝了一下 以後用到直接copy 先上圖: 主activity 復制代碼 代碼如下: pa
任何程序都是靜態代碼,我們把這些靜態代碼打包好,然後放到運行環境當中,通過事件流的驅動使這些代碼運行起來。Android的環境也不例外。靜態的代碼,在動態事件的驅動下,才
背景介紹在開發應用過程中經常會遇到顯示一些不同的字體風格的信息猶如默認的LockScreen上面的時間和充電信息。對於類似的情況,可能第一反應就是用不同的多個TextVi
簡介 項目一直是手工測試為主,加上一直是TV類應用,很多自動化工具都沒有針對TV類項目做很好的適配,所以只有自己動手了。主要針對項目的特殊性進行了部分改造,不一定適用於其