內容來源:2017 年 12 月 3 日,科大訊飛應用研發經理程坤在“IAS2017網際網路架構峰會”進行《訊飛輸入法Android架構演進與實踐》演講分享。IT 大咖說(微信id:itdakashuo)作為獨家視訊合作方,經主辦方和講者審閱授權釋出。
閱讀字數:3031 | 8分鐘閱讀
摘要
本次演講將分享訊飛輸入法Android版從最初開發到逐步發展成熟的過程中所面臨的各種挑戰以及經驗,還有架構的逐步演進過程。最後提到了團隊在元件化架構中的一些實踐。
架構演進歷程
架構演進概覽
訊飛輸入法初期採用的是簡單MVC,2012年3月份進行了分層重構,2014年12月份做了多程式的架構,最後2015年12月份開始嘗試元件化相關的工作。
架構如何演進
當架構與業務發展不匹配的時候就需要考慮改變當前架構,讓架構去適應業務。無論是組織架構還是軟體架構都需要面對業務問題,而如何解決這一問題是架構的核心目標。
軟體架構也要與組織架構相匹配,它們之間應該是一一對映的關係,也就是說系統設計的架構所對應的設計結構和組織結構是相等的。
架構的演進的關鍵在於平衡性,架構設計人員經常會追求完美,比如設計非常好的擴充套件性,追求極致的架構,一股腦的將最新最酷的技術放到架構中。這些做法是不是業務所需求的其實並不一定 ,所以架構的可靠性、擴充套件性包括安全性等是要做一定平衡的,考慮時間和成本上的問題。
產品初期
訊飛輸入法的專案於2010年7月份啟動,當時的開發人員僅有兩人。在2010年10月份的語音雲釋出會上訊飛輸入法要作為演示型產品展示,因此對產品的要求是非常高的。可以看到開發的時間其實只有短短的三個多月,而且當時很多功能都是首創並無產考。
簡單MVC
基於產品初期的這些挑戰,我們在開發的時候其實是沒有使用架構,而是優先考慮如何快速穩定的實現功能。下圖展示的是開發前的設計圖,上面是展現層,包含各種功能模組,右邊是業務邏輯,中間是拼音手寫和語音輸入的引擎,最下面是資料儲存。
根據專案開發過程中的經驗來看,當你不能判斷產品釋出出去後是否能存活或者能否達到預期的情況下,不要花太多的精力去做一個非常好架構,如何快速穩定的釋出產品才是核心目的,因此我們的建議是儘量複用。
產品快速發展前期
在產品的快速發展時期我們的主要工作就是補齊功能、優化效果,這時的開發人員也由原來的2人增加到了4人。開發過程中也出現了一些問題,由於當時都是各寫各的程式碼,導致程式碼的重用性很差,新功能開發成本高,維護起來困難。
基於這方面的問題我們向產品人員提出了放慢產品釋出節奏的要求,因此有了三個月的時間進行產品的重構。
分層重構
這次重構對產品進行了抽象分層,主要目的是為了複用。下面兩層與業務無關,工具層包含常用的工具類,框架封裝的是業務無關的通用業務能力。服務層和業務層則是和業務相關的,比如服務層的日誌應用了框架層日誌的能力,並融合了業務上的策略。
分層架構後開發效率獲得極大的提升,將原先比較差或不好維護的模組重新進行了梳理和優化,同時還封裝了很多了公共初始模組。
產品快速發展中期
產品經過迭代後功能越來越多,程式碼也越來越複雜,這時傳統的分層架構已無法滿足需求。因此我們將現有的10人團隊,分成了業務組和架構組,架構組主要負責效能和穩定性上的優化。
多程式架構
在架構組進行優化的過程中,我們將原先的架構調整成多程式架構。原先的輸入法只有一個程式,啟動的速度非常緩慢,程式崩了輸入法就無法使用。因此我們將輸入法分成5個程式,將使用者不常用的功能放在單獨的程式中,用完後立刻殺掉,也就是即用即走。另一點就是隔離,把原先後臺的一些功能,比如日誌、下載、推送等單獨剝離出來獨立成一個程式,那麼當這部分出現問題的時候就不會影響到主程式。
多程式的呼叫是非常麻煩的,因此我們摒棄了原先的單例模式,簡化了呼叫。
產品快速發展後期
產品發展後期團隊已經達到了20多人,管理起來很不方面,因此整個團隊被分成了4個業務團隊,4個架構團隊。這時需要考慮的是什麼樣架構才能匹配各個團隊的例行開發,同時還要支援更快的產品迭代。
為了保證快速迭代的過程中的產品穩定性,我們開始往元件化的架構上發展。
元件化的架構
目前開源的元件化框架非常多,要想實現這樣的框架其實沒有什麼成本。當時我們的關注點在並行開發上,做到團隊之間是隔離的,開發人員開發完功能後可以自行上線、測試、驗證。另一個關注點是動態更新,保證上線的時候框架支援動態更新能力。
這個過程我們發現元件化架構坑實在是太多了,還沒有或者準備做這方面的人員要想清楚是否一定要使用元件化架構。這裡有兩個概念,一個是並行開發,一個是並行釋出,可以根據自身的情況選擇做到哪一點為止。並行開發相對來說比較容易的,但是並行釋出難度就不一樣了,它涉及的不僅是客戶端,還有服務端、大資料、版本的管理和相容等各方面的問題。
元件化架構實踐
為什麼重複造輪子
較早的元件化框架有Atlas/ACDD、DynamicLoadApk、DynamicApk、Small,最近又出了兩個新的框架VirtualAPK、Replugin。雖然考察了眾多的框架,但是我們綜合考慮後還是決定自己動手實現,這是由於輸入法業務有其獨特性。輸入法不同於普通的App,它在鍵盤方面有著非常高的要求,而這恰恰是其他開源框架無法滿足的。
相容性
由於安卓系統的碎片化問題相容性上的處理非常麻煩,比如會出現手機在切換到橫屏狀態時輸入法顯示一半的情況,後來發現是因為螢幕切換的時候資源沒有重新整理,獲取到的還是原來的螢幕寬度,於是我們在ActivityThread內做了Hook。Hook本身的機制就是利用java的反射機制將API固有的實現替換成我們自己的。
啟動效能
啟動效能的問題主要出現在鍵盤啟動變慢,空間不足導致崩潰上。框架的啟動相比於原先做的事情更多了,花費的時間也更多。另外老使用者在進行升級的時候,元件啟動過程中會有一個配置檔案,列出元件清單配置元件啟動的優先順序,然後對它進行解壓再壓縮最後拷貝到相應的地方。可以看到因為要騰出另外的空間將元件提取出來進行解壓優化,這樣對於老款的ROM空間不夠的手機就很不友好。
基於以上的問題我們將原先的清單檔案改成class檔案,將應用啟動相關的部分歸為一個Dex,不相關的歸為另外的Dex,這樣啟動速度就能得到提升,因為在安裝時Dex就做了優化。
多程式
我們在程式的相互呼叫間新增了封裝層,像使用本地能力一樣使用跨程式的能力,包括遠端介面轉本地介面、遠端拉起、重連和狀態恢復。另外為了保證各元件能力能夠正常執行,讓元件自適應選擇程式。
工程結構
這裡的殼工程無任何程式碼,僅有指令碼及配置檔案,且只有唯一分支,一般不做修改。另外殼工程僅整合打包、整合除錯時使用,不使用repo、git submodule。
業務元件中的Bundle可獨立編譯除錯,打包產物有:測試apk、元件apk和aar,這些產物都會被上傳到Nexus私服。