Atlas是什么
Atlas是古希臘神話中的天神,是波士頓動力公司的機器人,借助搜索引擎,得以發(fā)現(xiàn)這個名詞背后許許多多的含義。在手機淘寶,Atlas是一個扎根于Android客戶端的一個組件化容器框架,相比神話中用手和頭支撐起蒼天的泰坦神族,Atlas在手淘默默無聞地承載著手淘上豐富業(yè)務(wù)的運行,伴隨著數(shù)不清的功能在用戶手中經(jīng)歷新老交替。
插件化的歷史
在阿里,在手機淘寶,移動客戶端的迭代更新可以說是整個移動互聯(lián)網(wǎng)從新起到繁榮的見證。伴隨著越來越多業(yè)務(wù)的誕生,越來越多的代碼開始往小小的客戶端涌入,而無論是iOS還是Android,各種客戶端的體積也經(jīng)歷著快速的增長。而由于客戶端的推廣成本較高,各個業(yè)務(wù)線都有很強的需求接入手機淘寶客戶端。所以在當(dāng)時航母戰(zhàn)略的背景下,迫切需要一種技術(shù)能把客戶端化整為零,這些模塊可以自由組合,并且當(dāng)部分功能變更時只需要更新對應(yīng)模塊。
我們在2012年底的時候就開始研究Android上的插件化技術(shù),并與2013年初把Atlas作為插件化框架接入手機淘寶?;诓寮哪芰?,一個普通的Android應(yīng)用可以低成本地轉(zhuǎn)為符合Atlas規(guī)范的插件,使Apk既可以以插件方式運行也可以獨立安裝運行。一個大的Android客戶端項目可以分割成數(shù)個插件,做到代碼隔離,降低了開發(fā)、維護、部署的成本。
當(dāng)時插件化的方案可分為三類:Web、WebApp、Native。各種方案在手淘內(nèi)都在有同學(xué)進行深入的研究。
Native形式的插件由于技術(shù)門檻較高,當(dāng)時業(yè)界采用的并不多見。但是由于其比WebApp有良好的性能和體驗,同時對于許多已經(jīng)成型的小客戶端推廣的迫切需求,尤其值得我們?nèi)ヌ剿鳌?/p>
上圖是當(dāng)時插件化的技術(shù)原理的簡圖,其實跟現(xiàn)在業(yè)界的插件化方案主要的設(shè)計思路基本一致。Atlas作為一個模擬的Android運行環(huán)境,每個插件以一個獨立的進程運行在各自的沙箱環(huán)境中。同時由于接入成本較低,其他的業(yè)務(wù)應(yīng)用基本可以算是零成本的方式接入到手淘環(huán)境,同時也可以隨時發(fā)布新的版本進行更新。
插件化的方案有其特有的優(yōu)勢,獨立的進程保證了業(yè)務(wù)的絕對隔離,同時也便于隔離風(fēng)險。聚劃算、天貓、彩票一個個獨立應(yīng)用開始起降于手淘這架航母。甜蜜的成長總是短暫,插件化發(fā)展很快,隨著手淘All In方案的深入,一些隱患開始展現(xiàn)。
組件化的誕生
伴隨著All In的進行,在手淘內(nèi)部發(fā)起了對大型app進行重構(gòu)的計劃。我們需要這樣一個框架:
↑新容器化結(jié)構(gòu)設(shè)想↑
基于對插件化框架對Android運行機制的理解,參考了OSGI在服務(wù)端框架,在開發(fā)IDE等領(lǐng)域”高復(fù)用、低耦合、可插拔”的優(yōu)勢,我們借鑒了OSGI規(guī)范開發(fā)了基于組件化的Atlas容器化框架。
下圖是基于組件化框架的系統(tǒng)結(jié)構(gòu)
解耦和依賴
依托classloader,組件的獨立性有了充分的保證,同時由于BundleClassloader保持了對PatchClassLoader的引用,使得宿主的中間件就如同普通APK開發(fā)一樣可以被組件簡單調(diào)用;同時組件之間除了基于AIDL的服務(wù)調(diào)用,組件之間提供了靜態(tài)依賴和運行時依賴,相互依賴的組件可以通過配置打成,這在一定程度上使某些偏功能的代碼或者UI的重用成為可能,同時配置的方式使組件之間的關(guān)系可以清晰追溯。
性能的演進
動態(tài)性的增強
diff
每次大版本的發(fā)版,集成平臺會保留該次發(fā)版的AP(Ap中記錄所有參與構(gòu)建的二方庫的版本,組件的依賴,混淆的mapping等各種信息),在手淘接下來每周一次的動態(tài)部署迭代中,動態(tài)部署的構(gòu)建會與之前的dex進行字節(jié)碼級別的diff,生成tpatch包,最終下發(fā)到用戶手機的patch僅包含變化class組成的dex和更改或者新增的資源文件
Bundle的Merge
patch收到后,客戶端的merge過程會根據(jù)patch信息,取到source 組件,source dex和patch dex進行dex合并,合成新的dex,同時變化的資源覆蓋老的資源最終形成新的組件。
中間件部署
動態(tài)部署能力的增強少不了對底層中間件的支持,部署的機制也與bundle截然不同
1. 中間件代碼部署
Dalvik和Art在merge過程中都會形成新patch zip,zip中dex以多dex的方式命名,patch dex為classes.dex,原始apk中的classes.dex以此根據(jù)序號+1放入zip中,不同的是dalvik由于preVerify的限制寫入zip中的源dex會先經(jīng)過處理,剔除在patchdex中已有的class,確保被patch的dex無法被打上preVerify的標(biāo)簽。
安裝過程中則多個dex依次dexopt,同時插入到dexpatch的第一位。而ART由于無preVerify校驗,但是Android到ART后原有的dexopt會改為dex2oat.
運行時代碼由原來的解釋執(zhí)行改為直接運行native代碼,之前的使用過程中發(fā)現(xiàn)單純得往前面追加patch的dex并不能完全解決動態(tài)部署的問題,dex2oat的過程優(yōu)化了class的執(zhí)行代碼,比如說內(nèi)斂,虛函數(shù)的校驗等。就有可能在運行過程中直接在native層執(zhí)行老的優(yōu)化過的class代碼而不是從新patch的dex中l(wèi)oad新的class去使用。
所以在ART上類似Dalivk,放入源dex,只是源dex不會做任何處理,然后合并在一起做一遍dex2oat,這樣能使新的class覆蓋老的class參與dex2oat并完成優(yōu)化的過程,
2. 中間件資源部署
資源部署基于overlay機制達成,不過dalivk上這個機制并不支持新增資源,在overlay的包里面如果讀到了一個資源,dalvik系統(tǒng)會去校驗該資源ID在base中值,如果不存在則拋錯,所以動態(tài)部署為了利用這一特性同時支持新增資源的需求,在打基線包的時候就在每個不同的type里面預(yù)留了128個資源ID供后續(xù)動態(tài)部署使用。
同時打動態(tài)部署的patch時會以之前的ID分配的內(nèi)容作為輸入,保證已有的資源分配到的ID保持不變,同時如果資源沒有發(fā)生變更,則剔除該資源,所以aapt dump resource patch的時候我們看到的INVALID TYPE 的資源段就表示沒有發(fā)生變更的資源,如果有發(fā)生了變更,且之前有這個資源,就會在原有的資源ID分配過去,同時新的資源文件打入patch包或者新的資源文本寫入arsc,如果是新增的資源,則使用預(yù)留的資源段進行分配。整個替換過程如下圖所示
Atlas動態(tài)部署的能力隨著手淘的需求不斷進行著演進,隨著整個框架的開源,自然需求會變得更加廣泛。比如在手淘內(nèi)部一周一發(fā)版的節(jié)奏下,動態(tài)部署對新增component的某些需求并不強烈,而開源后此類功能可能會尤為重要,所以接下來容器的更新迭代中此類的功能也會及時上線。擁抱開源社區(qū)會是Atlas重新審視自己,繼續(xù)發(fā)展的關(guān)鍵一步。
未來的路
相比于iOS,Android的推陳出新的速度是極快的。Google在Android系統(tǒng)的改進深入到方方面面,組件化的發(fā)展也需要緊跟Android步伐。
Atlas起步的時候MultiDex還未出來,解決方法數(shù)的問題就能帶來足夠的愉悅。現(xiàn)在Atlas基于組件化減少主dex方法數(shù)的同時,也支持主dex原生MultiDex的能力。目前Atlas內(nèi)部集成了MultiDex的能力,外部只需要打開MultiDex開關(guān)即可。這么做的目的一方面避免外部初始化實際的誤解,另一方面我們也意識到原生的mulitdex在性能上還有很多優(yōu)化的點,我們會在后面及時進行優(yōu)化。
在開發(fā)調(diào)試方面,Atlas容器框架基于動態(tài)部署的能力支持單個組件的獨立編譯調(diào)試,但是相比instantrun的速度,目前還有很大提高。而基于Instant Run改造的單組件秒級編譯也已經(jīng)在緊鑼密鼓的開發(fā)中,后續(xù)基于框架的獨立調(diào)試插件也將會在Android Studio插件庫中上線。
Android系統(tǒng)機制的演進也鞭策著動態(tài)部署前行。比如16年Android 7.0的混合編譯當(dāng)時也給Atlas造成了不小的挑戰(zhàn),服務(wù)于億級的UV,動態(tài)部署的能力的覆蓋面、到達率都還有進一步提升的空間,相比andfix等熱部署修復(fù)方案,動態(tài)部署需要重啟來打到更新的能力可能也需要有新的轉(zhuǎn)變。
市面上許許多多的容器框架,基本思路就大致兩三種。Atlas本身也沒有特別新的技術(shù),有的是基于已有的知識進行不斷的重組和優(yōu)化。在手淘內(nèi)部,追求更高的穩(wěn)定性和性能,億級的UV需要及其穩(wěn)定的容器來承載,同時Atlas也不斷嘗試新的方案,在創(chuàng)新與實用之間不斷權(quán)衡。
Atlas是一個結(jié)構(gòu)簡明的容器框架,通過盡量簡單的思路去解決移動應(yīng)用從開發(fā)到運維中的的桎梏,讓開發(fā)中的復(fù)雜簡單化,讓維護中的復(fù)雜簡單化,讓開發(fā)從流水線上忙碌的工作回到專注于對最終目標(biāo)的精耕細(xì)作,拋開嘈雜的干擾,撥開虛無縹緲,到達云和山的彼端!