微信Android客戶端架構演進之路

去年7月,筆者在InfoQ舉辦的ArchSummit深圳2014的架構師峯會上,分享了微信Android客戶端的架構演進史。能夠說,這是一個典型的Android應用在從小到大的成長過程當中的「踩坑」與「填坑」的歷史。互聯網的變化速度如此之快,1年的時間裏,能夠發生翻天覆地的變化。今天在這裏,從新和你們回顧微信客戶端架構的演進過程,以及其背後的開發團隊、流程的變化與思考。git

拓荒

微信1.0 for Android的測試版本於2011年1月發佈。這是微信Android客戶端的第一個版本,軟件架構採用早期標準的Android系統應用設計。github

【圖1】web

第一個版本是兩我的用了一個多月的時間開發出來的,其中一個仍是剛剛畢業沒多久的實習生。這個時期團隊一貧如洗,資源有限、經驗不夠,主導思想是,複雜的事情儘可能交出去作,保持最精簡的客戶端代碼。得益於Android應用開發簡單快速,從結構上看,這個時候其實尚未到須要特別設計的階段,是最原始、簡單的Android應用。數據庫

固然,再簡單的軟件也要考慮基本的設計思路。分層設計思想從這最先的版本開始引入一直到今天。回顧當時的設計,更像是MVP結合事件通知機制。從最上面由Activity組件組成的UI層(VIEW),往下到由NetScene組成的表現層(Presenter),再往下Network負責網絡長短鏈接與數據庫的通訊與Storage組成的存儲層。NetScene是一個網絡或者本地任務的基本單元,包括操做網絡作數據收發、協議編解碼,操做數據庫作各類聯繫人、消息模塊的讀寫。典型的例子如發送一條消息NetSceneSendMsg、作一次收信同步操做NetSceneSync。1.0版本的微信整個UI的activity可能不超過五個。緩存

這個階段,不須要作什麼「減法」,咱們的安裝包也只有354k。服務器

成長

微信的快速增加,從2.0版本開始第一次爆發。從語音版,到附近的人、漂流瓶,再到搖一搖。這個階段的咱們,彷佛將所有的時間和精力都放在新功能拓展上。新的Activity、新的NetScene加上新的Storage,在看似成熟的框架裏,一個功能就這樣完成了。簡單、快速、暴力。隨之而來的,是一些以前徹底沒有想到會可能出現的問題,讓最開始接觸Android開發的咱們措手不及。微信

早期版本由於經驗的問題,產品上不少功能不去想也不敢去想,版本開發的時間跨度也比較長。隨着開發經驗的積累和對產品方向的理解,3.0以後的每個小版本都處在一到兩週的高速迭代過程當中。網絡

追求更好的用戶體驗,更多豐富的功能是產品經理們永遠不會放棄的事情,尤爲是在新功能爲微信的新增用戶帶來了一次次爆發式的增加以後,更是沒法控制。功能的試錯頻率大大加速,機型覆蓋量上升後的兼容性問題也逐漸暴露。代碼量、內存佔用、安裝包體積迅速膨脹。而一樣處於發展中的Android系統,也給咱們埋下了不少坑,須要開發本身來實現、修復和優化。放在今天webview組件再也不是什麼問題,但在2.3以前的系統裏面都會存在嚴重的內存泄露。內存問題爲微信客戶端架構的第一次進化埋下了伏筆。架構

變革

在微信1.0時代的時候,咱們的關注點更偏向功能,隨着用戶增加,性能和穩定性問題逐漸浮上水面。2.0版本後,用戶反饋中微信消息推送不及時的比例在上升,做爲一款目標替代短信的即時通信應用,沒法及時收取別人發來的消息,這一點是很是致命的。app

Android 1.五、1.六、2.1在當時是主要的版本,那個時候是沒有今天的GCM的,甚至連C2DM也是2.2系統以後纔會有的。而谷歌服務在國內被屏蔽,在至關長一段時間內,不管是C2DM仍是GCM都沒法正常進行推送。沒有像APNS同樣穩定的推送通道可供Android平臺的應用使用,怎麼辦?本身實現。

國內網絡的特殊性,使得咱們再實現微信推送機制時,須要維持準確的心跳週期。若是一段時間沒有活動,運營商便會將長鏈接斷開以回收資源,這時服務器發消息給客戶端就接收不到了。進一步研究發現,運營商網絡的時間限制各個地區不一樣,有的地區有兩分鐘,有的地區有半個小時,這種狀況是不可接受的。咱們的解決方案是縮短心跳間隔,在網絡運營商把客戶端到接入點之間的鏈接斷開以前,我再發送一次心跳,主動維持住這個長鏈接的活性。這個咱們稱之爲長鏈接的保活。關於長鏈接的保活策略,微信也作過屢次優化,這裏另文介紹,再也不贅述。

還記得前面說微信的膨脹嗎?代碼、內存、apk大小都在膨脹,這其中,內存對消息收發的影響很關鍵。Android運行時的擇優置換機制,會選取佔用資源最多的程序結束掉,除了微信本身功能膨脹致使內存佔用加大以外,前面說的不省心的webview,還會給咱們在內存問題上挖坑。而結果就是在用戶手機運行APP比較多的時候,微信會被系統殺掉回收資源,消息收取不及時的問題就出來了。

如何解決?方法其實很多,微信選擇的,是輕重分離的思路。經過在微信3.5版本時候作的架構重構,實現了不受功能增加、系統缺陷影響的穩定推送方案。

【圖2】

對比v1.x版本的微信客戶端架構圖,咱們將右下角Network的部分用輕重進程分離的思想,獨立到一個單獨的進程(:push)中,而上面兩個層級依然跑在微信的主進程(:worker)中。而對於有內存泄露問題的webview或者其餘不頻繁使用的功能,再把其分離到獨立的工具進程(:tools)中。經過分離進程,微信第一次重構解決了系統由於微信資源消耗,主動幹掉微信服務的困境。分離後的push進程內存佔用以及被系統kill回收的概率大幅下降,而對於worker和tools進程,咱們再也不要求其必定存在,只在用戶收到消息,或者進入h5相關功能界面時存在便可。這個版本的架構變動基本達成了咱們設定的目標,不管是電量仍是平均待機內存消耗上都大幅度降低,從內存上來看降低了70%,電量的話也比競品和咱們前一個版本有好轉。

固然任何事物都有兩面性。這一次架構的改變存在的問題逐漸在咱們後面的開發當中暴露出來。好比進程每一次都要從新加載,裏面全部的Cache、圖片、界面所有要從新去執行一遍一樣的代碼,每一次加載內存都須要從新消耗時間。而啓動速度變慢,則是最明顯,用戶最能感知的問題。「地球出現頻率高了」是咱們在這一時期常常聽到的聲音。而系統資源的消耗實際上比原來單進程的時候會更多,每個進程都須要額外多佔用一份虛擬機部分的內存。

這些缺點在3.5版本時代是能夠接受的。從監測結果上看,啓動速度變慢將微信的啓動速度延長到了秒級,從原來的300-500毫秒到如今800-1000毫秒的級別。主要的圖片緩存失效,則經過異步加載、解碼、展現解決。拉長來看,微信的主進程資源會被自動回收,平均內存佔用相比以前仍是降低的。即使在今天來回顧,依然能夠看到,輕重進程拆分的思路是正確的選擇。即使系統層面各類各樣的bug逐漸減小,但應用的迭代使得功能必定不會減小。爲了保證圖片、資源類在速度上的體驗,內存的消耗也只會更大,是空間換時間的思路。而輕重分離,保證了核心服務在設備資源發生競爭時最大機率存活的同時,不形成對設備過多的資源佔用。典型的場景就是用戶開啓遊戲、視頻錄製通話等大型應用,做爲常駐應用,不該該搶佔額外的有限資源,須要作到「該放手的時候就放手」。

進化

很快,微信的架構演進進入了第三個階段(v3.x)。微信4.5版本的開發過程當中,出現了沒法安裝在一部分Android 2.3如下系統的機器上,當時2.3的市佔率還在50%以上。試想一下,市場上一半用戶的手機不能安裝使用微信,這對咱們是一個致命打擊。放在今天看,2.3早已被淘汰,但在當時,咱們不可能停下來等待,不管從開發和產品來講都是不可接受的。技術分析的直接緣由就是,微信的發展速度太快,觸發到Android虛擬機機制的設計缺陷。

兩個問題,一是單dex 65535方法數限制,二是線性內存分配器(LinearAlloc)限制。今天的Android開發者看到這兩個限制都不會陌生。前者是由於Android的早期設計中,對dex文件中方法id用16位整型標記,單個dex文件中的方法數沒法超過65535,eclipse環境中生成不了未作過proguard的debug apk。後者則是dalvik虛擬機用來加載類的堆內存大小被硬編碼了,2.3如下是5M,2.3以上是8M,微信沒法安裝的緣由就是由於這個堆內存被耗盡致使dexopt失敗。

今天來看,Google已經給出了一些可靠的解決方案,輔以更加先進的gradle + Android Studio,開發者們可能根本不會再遇到這兩個經典問題,。官方的MultiDex分dex機制解決了方法數限制的問題,其中main dex最小化原則,結合dalvik LinearAlloc heap size調整(修改到了16M),使得dexopt的失敗概率大幅降低。而art的出現完全再也不存在LinearAlloc這樣的限制。回過來再看,那個時代裏微信是如何經過軟件架構調整解決這些問題的。

微信在高速發展過程中,到5.0的時候已經有不少功能,而其中一些功能,隨着用戶羣體、產品設計等因素變化,用戶使用的頻率在改變。以前試錯的一些功能,也大量存留在微信版本中。這些不常使用的功能不該該始終佔用程序資源,從架構上進行縱向分離,保證主要場景的體驗,是這一時期的主要設計思路。

【圖3】

要作的第一步就是解耦。微信這類社交型應用,在用戶數據、關係、消息等結構上存在着各類各樣複雜的依賴,這些依賴相比工具型的軟件來講,調用頻率更高,性能要求也更高。想作到完美的拆分不是一件容易的事,可是不完美的拆分倒是能夠達成的。輕重分離的思想再一次被應用,這一次是在代碼模塊的使用和組織上。保證主app功能的快速和穩定,將附屬的新功能分離在獨立的插件工程(p_XX)中,每一個插件有獨立的UI界面邏輯和資源、存儲及網絡協議編解碼處理邏輯,經過共用統一的基礎庫接口訪問網絡服務。

將微信功能解耦爲插件,一個插件內僅向下依賴。插件最後編譯出來會是一個jar包,其內包括的實際內容是對應的dex。這裏須要注意的是,插件並不須要有獨立的進程空間,而是根據該插件實際的場景決定其運行的實際進程,絕大多數狀況下,插件是和主app功能共享多進程載體的。

v3.x架構的改造工做量對當時的咱們來講很大,從最開始4.3版本發現dex limit和LinearAlloc limit到5.0版本成型作第一次的驗證,咱們花了8個月時間,解耦出來的工程項目有60個以上。4.5版本將附近的人分離出去是做爲一次試驗,爲5.0這一大版本填完了坑。5.0版本是微信歷史上很是重要的一環,從這個版本開始引入了遊戲、支付和更加完善的公衆帳號體系。

這種設計思路不是微信獨創,如今回顧也並不複雜。若是你的產品歷史功能很少,迭代不是很快,能夠所有人停下來1到2個月集中一次重構搞定。但對於微信來講,但這一版的架構變動,更像是在給天上飛着的飛機換髮動機,由一支10來我的組成的團隊完成。互聯網的快速、敏捷給處在「創業」階段的咱們新的挑戰。如何作到呢?要保證不給處於高度需求壓力下的開發人員增長架構變更的額外負擔,首先要作的就是不要讓他們重複修改代碼,無縫遷移到新的架構。

1、建立必要的工具和規範。微信在4.3發現問題以前,一直堅持着很是好的開發效率優化思想,代碼自動生成起到了很大的幫助。團隊內部使用的自研的代碼生成工具autogen,經過簡單的xml定義,便可生成所須要的存儲、協議編解碼、事件機制代碼。這使得咱們具有了比較輕鬆解耦的前提。

2、新的架構要求開發者在作新功能時,使用獨立插件子工程,好的工程模板能夠事半功倍。早期傳承下來的分層設計,也使得開發人員在先後兩種開發模式下的學習成本降到最低。對應的編譯和開發調試工具。

3、對於歷史實現的功能特性,儘可能經過反射等一些技巧,來保證不須要大規模重寫代碼,「先抗住,再優化」。不要一開始就追求完美,先活下來。直到5.一、5.2版本,咱們才基本上所有完成這一次程序架構調整。

4、人。架構調整是必需要作的事,可是做爲發起者,也不能只從理論角度去強硬推進。減小開發者的工做量,而不是增長,站在開發者的角度想問題,每每會獲得很是積極的響應。

不一樣的客戶端架構時期,背後的團隊和開發模式也會有所不一樣。對比三個版本的客戶端架構,v1.x和v2.x的時候比較適合小型團隊、沒有特別複雜或者反覆需求的客戶端進行快速開發。單trunk主線開發便可知足。每次發佈後,拉出來一個對應的release branch,若是發現有bug或者小優化須要修改,直接在這個release branch上修改、測試、發佈上線。這一時期release branch一般保持在兩個之內,當前版本和前一版本。當前版本是爲了線上問題的快速發佈,而前版本則是爲了修復一些廠商渠道預裝的問題。

【圖4】

v3.x就比較適合中到大型團隊,解耦以後,能夠支持多個團隊的並行開發,也能夠知足多個版本的同時開發和發佈。微信的產品經理和客戶端開發人員的比例大概是1.5+,也就是說產品經理會比開發人員多50%以上。開發人員會面對產品各式各樣的需求須要實現,不少需求處於原型或者是設計階段,而有些即使開發完成,也須要和老大致驗修改。這種不穩定狀態的需求,每每是今天說最終肯定,但2個小時後就可能要修改,甚至臨上線前均可能說這個功能被砍掉,不要發佈了。不穩定的需求會帶來不穩定的代碼實現,質量也無法控制。經過多迭代多版本多分支並行開發,只有全部人體驗過最終確認沒問題能夠上線,纔會合入發佈版本上線。

這個過程像什麼?像開源項目的開發。

開放

進入到2015年後,微信在軟件架構上逐漸趨於平穩。在v3.x原有插件加載基礎上,研究了更多行業內Android應用的技術架構。結合官方MultiDex的實現,增長動態熱補丁功能,經過終端的運營系統,實現了微信客戶端補丁版本更新48小時90%+覆蓋率。編譯系統也從buck+修改成微信自研的builder構建,支持LinearAlloc和methods/fields count的實時計算,以及融合了MultiDex與微信插件模式的dex自動分包。在v2.x架構輕重分離的多進程思路基礎上,進一步優化實現了push的在收信條件下的「lightpush」運行模式。在僅消耗push進程低內存的條件下,實時收取新消息通知,避免對進行中的遊戲進行資源搶佔的同時,又能夠及時收取消息。

【圖5】

更重要的是,咱們開始將目光轉移到開源的開發模式上。v3.x的並行開發模式,在svn下已再也不適應。2015年上半年開始微信Android客戶端團隊開始轉向git,充分發揮git在多團隊並行開發下的優點。內部也放棄了沿用許久的ant + eclipse,全面轉向gradle + Android Studio的分佈式構建思想。經過內部開源,微信內的公共組件已經能夠經過maven在不一樣的開發團隊中共享並隨時使用。或許某一天,咱們也能夠在github上看到」WeChat Mobile Dev」的身影。

相關文章
相關標籤/搜索