Android客戶(hù)端的架構(gòu)不論如何演變,架構(gòu)設(shè)計(jì)的出發(fā)點(diǎn)總是離不開(kāi)兩點(diǎn),一是提高開(kāi)發(fā)效率,二是降低維護(hù)成本。阿劉基于這兩點(diǎn),主要分享了in Android客戶(hù)端的架構(gòu)演變。以下是正文:
2014年6月份,in發(fā)布了個(gè)版本。到目前為止,已經(jīng)經(jīng)歷了幾十個(gè)版本的迭代。在1.0時(shí)代,APP的特點(diǎn)是小、快、靈。當(dāng)時(shí)產(chǎn)品邏輯并不復(fù)雜,投入的資源不是特別多。因?yàn)樘幱谔剿髌冢援a(chǎn)品的迭代非???,為了與之適應(yīng),in采用了簡(jiǎn)單的單工程的形式組織整個(gè)產(chǎn)品結(jié)構(gòu),高結(jié)構(gòu)的層次也只有幾層,非常淺,如圖1所示。
為了兼容H5跳轉(zhuǎn),in參考了H5的一種路由協(xié)議作為跳轉(zhuǎn)的支持。該時(shí)期,in的用戶(hù)量增長(zhǎng)迅速,1.0末期已經(jīng)達(dá)到了近2500萬(wàn)。在這個(gè)框架下,in一直衍生到1.9版本,這段時(shí)期使用輕量結(jié)構(gòu)比較適合小步快走,即迭代非常快的形式,但這種形式存在一個(gè)明顯的缺點(diǎn),即擴(kuò)展性較差,個(gè)別類(lèi)臃腫,不適合協(xié)同開(kāi)發(fā)。隨著業(yè)務(wù)邏輯愈發(fā)復(fù)雜,參與人員不斷增加,如何提高協(xié)同開(kāi)發(fā)效率成為當(dāng)務(wù)之急。
in在2.0時(shí)代有三個(gè)亟需解決的問(wèn)題:
產(chǎn)品邏輯日趨復(fù)雜。復(fù)雜表現(xiàn)在兩方面:一是演進(jìn)非常快;二是反復(fù)。這是致命的,因?yàn)殚_(kāi)發(fā)過(guò)程中PD可能會(huì)臨時(shí)做一些需求上的變更。因此,此時(shí)的框架必須適應(yīng)產(chǎn)品的復(fù)雜化;
代碼復(fù)用性差。1.0時(shí)代,新模塊的開(kāi)發(fā)主要基于原有的模塊,僅僅在原有的模塊上做一個(gè)很小的改動(dòng),代碼實(shí)現(xiàn)上卻需要進(jìn)行大幅度的調(diào)整;
業(yè)務(wù)邏輯與基礎(chǔ)功能雜糅在一起。因?yàn)闃I(yè)務(wù)上的一些變更常常會(huì)觸及底層的東西,導(dǎo)致穩(wěn)定性不足。
基于這三點(diǎn),in在2.0時(shí)代進(jìn)行了如圖2所示的重構(gòu)。
首先,將所有業(yè)務(wù)分成獨(dú)立的模塊,同時(shí)考慮產(chǎn)品邏輯中可能沒(méi)有想到的模塊。以in為例,in有圖片詳情頁(yè),但從產(chǎn)品邏輯看,產(chǎn)品分為個(gè)人中心、話(huà)題、品牌站等模塊,這些模塊都有各自的圖片詳情頁(yè),圖片詳情頁(yè)并不是一個(gè)獨(dú)立的業(yè)務(wù)線(xiàn)。初期,in嚴(yán)格按照產(chǎn)品線(xiàn)去劃分業(yè)務(wù)邏輯模塊,所以開(kāi)發(fā)出好幾套圖片詳情頁(yè),這就是代碼復(fù)用性差。經(jīng)驗(yàn)教訓(xùn)是,模塊不應(yīng)完全由PD決定,我們必須非常熟悉產(chǎn)品結(jié)構(gòu),清楚有沒(méi)有共性的模塊可以單獨(dú)抽離出來(lái)。
其次,業(yè)務(wù)模塊必須具有一定的配置性,即可擴(kuò)展性。沿用剛才的例子,我們希望個(gè)人中心的圖片詳情頁(yè)具有顯示用戶(hù)打上去的標(biāo)簽、貼紙等的功能,但品牌詳情頁(yè)可能并不需要這類(lèi)功能,所以業(yè)務(wù)模塊必須達(dá)到可配置。分好業(yè)務(wù)模塊后,各個(gè)模塊之間相互獨(dú)立,但必然存在公共的功能,因此需要有一個(gè)底層的公共類(lèi)庫(kù)做支持。公共類(lèi)庫(kù)主要包括了一些基礎(chǔ)功能,比如網(wǎng)絡(luò)請(qǐng)求、圖片解析、本地日志系統(tǒng)等。
后,in引入了許多第三方功能,而公共模塊是一個(gè)獨(dú)立工程。in允許公共模塊直接使用第三方庫(kù),但不允許其它模塊單獨(dú)使用。因此,第三方庫(kù)達(dá)到統(tǒng)一管理。同時(shí),in還沿用并強(qiáng)化了1.0時(shí)代的路由協(xié)議,推送可以通過(guò)這套協(xié)議跳轉(zhuǎn)到推送頁(yè)面。
到此,in基本解決了之前談到的三個(gè)問(wèn)題,更重要的是提高了QA測(cè)試效率。
以往需要等所有功能開(kāi)發(fā)完成才能交付給QA,分模塊后,每一個(gè)業(yè)務(wù)模塊都可以不依賴(lài)其他模塊獨(dú)立運(yùn)行。當(dāng)一個(gè)模塊自己的業(yè)務(wù)開(kāi)發(fā)完成后,都可直接交付給QA。但與此同時(shí),產(chǎn)品又產(chǎn)生了新的問(wèn)題,即公共類(lèi)庫(kù)臃腫,難維護(hù),遷移成本高。
2.0時(shí)代,in的用戶(hù)量從1.0時(shí)代的近2500萬(wàn)增長(zhǎng)到7000萬(wàn)。in意識(shí)到每一次小的更新都會(huì)影響用戶(hù)的體驗(yàn),因此告別了原先快速迭代的發(fā)展模式,轉(zhuǎn)為求穩(wěn)。從開(kāi)發(fā)的角度來(lái)說(shuō),是要提供更優(yōu)質(zhì)的服務(wù)。
針對(duì)2.0時(shí)代產(chǎn)品公共類(lèi)庫(kù)臃腫的問(wèn)題,in在框架上做了如圖3所示的改進(jìn)。首先,上層沿用2.0時(shí)代的形式,但對(duì)公共模塊進(jìn)行拆分,將公共業(yè)務(wù)抽成代理層,并且引入服務(wù)化的概念,將每一個(gè)機(jī)組功能都抽成獨(dú)立的服務(wù),比如網(wǎng)絡(luò)請(qǐng)求、圖片上傳,本地日志等。這一版改進(jìn)后,服務(wù)都被獨(dú)立抽出,相互之間是隔離的,每個(gè)服務(wù)都可以交由不同的人去維護(hù),內(nèi)部高類(lèi)聚。
與此同時(shí),每個(gè)服務(wù)都需要有容錯(cuò)性,每個(gè)模塊都需要有兜底方案,保證自己的輸出是穩(wěn)定的,自己內(nèi)部的問(wèn)題不會(huì)影響其他服務(wù)。
另外,底層服務(wù)很少被上層的業(yè)務(wù)代碼入侵,可盡量通過(guò)協(xié)議或者是API的形式支持上層的業(yè)務(wù)邏輯,做到輕量化級(jí)的接入。in還對(duì)第三方庫(kù)做了封裝,將其通過(guò)代理的模式與自己的業(yè)務(wù)代碼隔離,這樣就可以靈活地替換第三方類(lèi)庫(kù),并且大大降低維護(hù)成本。
服務(wù)化過(guò)程中,in沿用了之前的通信模塊,并且加入了一個(gè)統(tǒng)計(jì)框架。這個(gè)框架著重突出了服務(wù)化的概念,并且是本地服務(wù)化。它的優(yōu)勢(shì)在于非常的獨(dú)立,且具有很高的擴(kuò)展性,每加入一個(gè)新的服務(wù),都不會(huì)影響到其他的服務(wù),并且在整個(gè)架構(gòu)的層面上來(lái)講,每一個(gè)服務(wù)之間相互依賴(lài)的關(guān)系、調(diào)用的順序都可以很快地整理出來(lái)。同時(shí),它還給in的產(chǎn)品矩陣打下了一個(gè)很好的基礎(chǔ),未來(lái)如果推出一些新的APP,需要引用in老的代碼時(shí),只需要選擇需要接入的那些服務(wù),就能很快理出新的APP的架構(gòu)圖,并且配置起來(lái)。
圖4為in內(nèi)統(tǒng)計(jì)框架,大的特點(diǎn)是自動(dòng)化、無(wú)侵入式。業(yè)界很多統(tǒng)計(jì)框架在路徑統(tǒng)計(jì)層面,主要是統(tǒng)計(jì)Activity層以及Fragment層,但是in的很多頁(yè)面是通過(guò)View等其他形式實(shí)現(xiàn)的,因此無(wú)法通過(guò)現(xiàn)有的一些統(tǒng)計(jì)框架進(jìn)行頁(yè)面統(tǒng)計(jì)。對(duì)此,in把所有的頁(yè)面都抽成layer的抽象概念,把所有的layer通過(guò)用戶(hù)的行為路徑壓到一個(gè)layer棧內(nèi),終以一個(gè)列表的形式發(fā)到服務(wù)器,然后在服務(wù)器建立一個(gè)數(shù)據(jù)倉(cāng)庫(kù),再通過(guò)BI部門(mén)整理數(shù)據(jù)倉(cāng)庫(kù)得出每個(gè)用戶(hù)的實(shí)際瀏覽路徑,包括每個(gè)頁(yè)面的留存等。
模塊內(nèi)的解耦
耦合存在每個(gè)模塊內(nèi)。業(yè)界很常見(jiàn)的是用MVC、MVP等模式進(jìn)行一定的解耦,in主要用MVP模式。為什么in之前Activity常常寫(xiě)得特別臃腫?因?yàn)樗粌H做了Model層的事,而且做了表現(xiàn)以及控制上的事。解決辦法是把View層單獨(dú)抽離,由Fragment去做View層的展現(xiàn),而Activity層只專(zhuān)注于對(duì)數(shù)據(jù)的處理,實(shí)現(xiàn)View層跟Model層之間不直接交互,而是通過(guò)一個(gè)接口的形式進(jìn)行溝通。
灰度發(fā)布機(jī)制
灰度發(fā)布機(jī)制主要是為了支持產(chǎn)品的A/B Test。in的產(chǎn)品越來(lái)越復(fù)雜,用戶(hù)量越來(lái)越多,為了實(shí)驗(yàn)性的功能不影響所有的用戶(hù)體驗(yàn),只能允許一部分特定用戶(hù)看到新功能,而這需要通過(guò)代碼層做控制,即灰度發(fā)布機(jī)制。如圖5所示,灰度發(fā)布主要在業(yè)務(wù)層之上,它的配置全部由服務(wù)器端決定,確保每個(gè)業(yè)務(wù)都可以做到灰度發(fā)布。
模塊間通信
模塊增加后,模塊間通信成為一個(gè)大問(wèn)題,因?yàn)槟K之間是不可見(jiàn)的。兩個(gè)解決辦法:是通過(guò)一套反射機(jī)制達(dá)到每個(gè)模塊間相對(duì)可見(jiàn);第二是建立一套自己的基于觀察者、訂閱者模式的消息分發(fā)機(jī)制,in的這套機(jī)制主要參考了EventBus以及谷歌近開(kāi)源的一個(gè)安卓響應(yīng)式框架Agera類(lèi)似于RxAndroid,這個(gè)框架能幫助模塊間通信的現(xiàn)成模型的構(gòu)建。另外,模塊間通信有一個(gè)Sticky機(jī)制,問(wèn)題就在于當(dāng)頁(yè)面未打開(kāi)之前,數(shù)據(jù)已經(jīng)先到了,那么該如何解決?就是通過(guò)Sticky機(jī)制,它能確保數(shù)據(jù)先到,頁(yè)面再打開(kāi)的時(shí)候,數(shù)據(jù)能順利下發(fā)。
每一套框架都有自己的特點(diǎn),但是萬(wàn)變不離其宗,主要的是要適合當(dāng)前的項(xiàng)目規(guī)模和體量,更好的與業(yè)務(wù)結(jié)合在一起,提高開(kāi)發(fā)效率,降低維護(hù)成本。
本站文章版權(quán)歸原作者及原出處所有 。內(nèi)容為作者個(gè)人觀點(diǎn), 并不代表本站贊同其觀點(diǎn)和對(duì)其真實(shí)性負(fù)責(zé),本站只提供參考并不構(gòu)成任何投資及應(yīng)用建議。本站是一個(gè)個(gè)人學(xué)習(xí)交流的平臺(tái),網(wǎng)站上部分文章為轉(zhuǎn)載,并不用于任何商業(yè)目的,我們已經(jīng)盡可能的對(duì)作者和來(lái)源進(jìn)行了通告,但是能力有限或疏忽,造成漏登,請(qǐng)及時(shí)聯(lián)系我們,我們將根據(jù)著作權(quán)人的要求,立即更正或者刪除有關(guān)內(nèi)容。本站擁有對(duì)此聲明的最終解釋權(quán)。