柚子快報(bào)激活碼778899分享:GitHub標(biāo)星9
柚子快報(bào)激活碼778899分享:GitHub標(biāo)星9
基本解耦方案
組件化的第一步就是對要拆出去的組件進(jìn)行解耦,常見解耦方式有以下幾種:
(1) 公用代碼處理
基礎(chǔ)業(yè)務(wù)邏輯分別拆成基礎(chǔ)組件 自身邏輯完整、用于完成某一特定功能、不含業(yè)務(wù)邏輯的一組代碼,獨(dú)立成 SDK 代碼量很小不足以拆分成單獨(dú)拆分的代碼和資源,我們統(tǒng)一放在一個(gè)專門建立的 common 組件中,并且嚴(yán)格限制 common 組件的增長。隨著組件化的逐漸進(jìn)行,common 應(yīng)該逐漸變小而不是增大。 碰巧被共同使用的一些代碼和資源片段,通常它們被復(fù)用只是因?yàn)楸婚_發(fā)人員搜索到而直接使用了,很多時(shí)候某個(gè)資源已經(jīng)被 A 業(yè)務(wù)聲明了前綴,但是由于沒有隔離,仍然會不可避免的被他人在 B 業(yè)務(wù)中強(qiáng)行復(fù)用,這時(shí)候如果 A 業(yè)務(wù)方要進(jìn)行一些修改,B 業(yè)務(wù)就會受到影響 —— 這種情況我們允許直接復(fù)制
(2) 初始化
有些組件有在應(yīng)用啟動(dòng)時(shí)初始化服務(wù)的需求,而且很多服務(wù)還是有依賴關(guān)系的,最初我們?yōu)槊總€(gè)組件都添加了一個(gè) init() 方法,但是并不能解決依賴順序問題,需要每個(gè)組件都在 app 工程中按順序添加初始化代碼才能正常運(yùn)行,這使得不熟悉整套組件業(yè)務(wù)的人很難建立起一個(gè)可以獨(dú)立運(yùn)行的組件 app。因此我們開發(fā)了一套多線程初始化框架,每個(gè)組件只要新建若干個(gè)啟動(dòng) Task 類,并在 Task 中聲明依賴關(guān)系即可:
App 只需知道自己依賴什么組件即可,在編譯 app 時(shí) gradle 插件會自動(dòng)定位到所有的 Task,并運(yùn)行時(shí)生成依賴圖,按依賴順序啟動(dòng) Task:
這樣就解決了組件在主工程中堆積初始化代碼的問題,在簡化了代碼的同時(shí)還有加快啟動(dòng)速度的功效。
(3) 路由
界面間使用 Url 進(jìn)行跳轉(zhuǎn),不但實(shí)現(xiàn)了解耦,也統(tǒng)一了各端的頁面打開方式。我們實(shí)現(xiàn)了一套靈活小巧的路由框架 ZRouter,它支持多組件、路由攔截、AB Test 、參數(shù)正則匹配、降級策略、任意參數(shù)傳遞以及自定義跳轉(zhuǎn)等功能,可以自定義路由的各個(gè)階段,完全滿足了我們的業(yè)務(wù)需求。
(4) 接口
除了頁面間的跳轉(zhuǎn),不同業(yè)務(wù)之間不可避免的會有一些調(diào)用,為了避免組件的直接通信,通常都是使用接口依賴的方式。我們實(shí)現(xiàn)了一個(gè) Interface Provider 來支持接口通信,它可以通過運(yùn)行時(shí)在動(dòng)態(tài)注冊一個(gè)接口,同時(shí)也實(shí)現(xiàn)了對于 ServiceLoader 的支持。只要一方組件將通信接口暴露出來,使用方就可以直接使用接口進(jìn)行調(diào)用。
動(dòng)態(tài)注冊接口
Provider.register(AbcInterface.class,new AbcInterfaceImpl())
獲取實(shí)例并調(diào)用
Provider.get(AbcInterface.class).doSomething()
(5) EventBus
這個(gè)自不必說,雖然說濫用是一個(gè)問題,但是有些場景下,使用事件還是最為方便簡單的方式
(6) 組件 API 模塊
上面提到的接口和事件以及一些跨組件使用的 Model 放到哪里好呢?如果直接將這些類下沉到一個(gè)公共組件中,由于業(yè)務(wù)的頻繁更新,這個(gè)公共組件可能會更新得十分頻繁,開發(fā)也十分的不方便,所以使用公共組件是行不通的,于是我們采取了另一種方式——組件 API :為每個(gè)有對外暴露需求的組件添加一個(gè) API 模塊,API 模塊中只包含對外暴露的 Model 和組件通信用的 Interface 與 Event。有需要引用這些類的組件只要依賴 API 即可。
[圖片上傳中…(image-a39cd0-1598345691997-6)]
一個(gè)典型的組件工程結(jié)構(gòu)是這個(gè)樣子:
以上圖為例,它包含三個(gè)模塊:
template :組件代碼,它包含了這個(gè)組件所有業(yè)務(wù)代碼
template-api:組件的接口模塊,專門用于與其他組件通信,只包含 Model、Interface 和 Event,不存在任何業(yè)務(wù)和邏輯代碼
app 模塊:用于獨(dú)立運(yùn)行 app,它直接依賴組件模塊,只要添加一些簡單的配置,即可實(shí)現(xiàn)組件獨(dú)立運(yùn)行。
組件半自動(dòng)拆分
有了解耦的方法,剩下的就是采取行動(dòng)拆分組件了,拆組件是一個(gè)很頭疼的問題,它非常考慮一個(gè)人的細(xì)心與耐心,由于無法準(zhǔn)確知道有哪些代碼要被拆走,也不能直觀的知曉依賴關(guān)系,移動(dòng)變得非常的困難且容易出錯(cuò),一旦不能一次性拆分成功,到處都是編譯錯(cuò)誤,便只能靠人肉一點(diǎn)一點(diǎn)的挪。
工欲善其事,必先利其器。為了解決這個(gè)問題,我們開發(fā)了一個(gè)輔助工具 RefactorMan: 它可以遞歸的解析出工程中所有源碼的引用和被引用情況,同時(shí)會根據(jù)預(yù)設(shè)規(guī)則自動(dòng)分析出所有不合理的依賴,在開發(fā)人員根據(jù)提示解決了不合理依賴之后,即可將組件一鍵移出,大大減少了拆組件的工作量。我們在組件化初期曾經(jīng)走過一些彎路,最初拆出的八個(gè)組件工程的的部分源碼經(jīng)歷了幾次的反復(fù)移動(dòng)才得出最優(yōu)解,而有了 RefactorMan,我們可以面對反復(fù)的拆分和組合組件有恃無恐。
Bonus :由于可以分析和移動(dòng)資源,所以額外獲得了清理無用資源的功能。
聯(lián)合編譯完整包
單獨(dú)運(yùn)行組件 app 并不能完整的覆蓋所有的 case,尤其是在給 QA 測試的時(shí)候,還是需要編譯完整的主工程包的,所以我們需要一個(gè)直接編譯完整包的方案:
最初我們的實(shí)現(xiàn)方式只針對組件,比較簡單:
首先在 setting.gradle 中動(dòng)態(tài)引入組件 module:
def allComponents = [“base”, “account” … “template” …]
allComponents.forEach({ name ->
if (shouldUseSource(name)) {
// 動(dòng)態(tài)引入外部模塊
include “?{name}”
project(“?{name}”).projectDir = getComponentDir(name);
}
})
然后在 app/build.gradle 中切換依賴,需要將所有被間接依賴的組件全部 exclude 以防止同時(shí)依賴了一個(gè)組件的 module 和 aar:
allComponents.forEach({ name ->
if (shouldUseSource(name)) {
implementation(project(“?{name}”)) { exclude group: COMPONENT_GROUP }
} else {
implementation(“
C
O
M
P
O
N
E
N
T
G
R
O
U
P
:
{COMPONENT_GROUP}:
COMPONENTG?ROUP:{name}?{versions[name]}”) { exclude group: COMPONENT_GROUP }
}
})
由于所有組件的 group 都是一樣的,所以這樣做并沒有什么問題,但是后來一些基礎(chǔ) SDK 也出現(xiàn)了這種需求,這時(shí)候就需要一種通用的源碼依賴方案,因此做了一下修改,直接使用 gradle 提供的依賴替換功能,只需要修改 setting.gradle 即可:
// … 忽略讀取配置代碼 …
configs.forEach { artifact, prj ->
include “?{prj.name}”
project(“?{prj.name}”).projectDir = new File(prj.dir)
}
gradle.allprojects { project ->
if (project == project.rootProject) {
return
}
project.configurations.all {
resolutionStrategy.dependencySubstitution {
configs.forEach { artifact, prj ->
// 在這里進(jìn)行替換
substitute module(artifact) with project(“?{prj.name}”)
}
}
}
}
而 build.gradle 的依賴寫法與普通的工程完全一樣。
普通狀態(tài)下的的主工程:
源碼引用 template 組件后的主工程:
這樣我們就可以像之前在單工程中一樣寫代碼了。
得益于源碼引用,我們直接在提交組件代碼的時(shí)候,CI 會自動(dòng)聯(lián)合主工程編譯出完整包,QA 會根據(jù)完整包進(jìn)行測試,在測試通過后即可自動(dòng)發(fā)布到公司的倉庫,并通過內(nèi)部的集成平臺集成到主工程
小 tip :工程 .idea/vcs.xml 中定義了當(dāng)前工程關(guān)聯(lián)的 Git 倉庫,可以在聯(lián)合編譯的同時(shí)通過修改 vcs.xml 來把組件目錄也關(guān)聯(lián)到主工程 Git 配置中,在開發(fā)過程中就可以使用 Android Studio 的內(nèi)置 Git 功能了
包含子業(yè)務(wù)線的組件
我們當(dāng)前的組件,絕大部分是一個(gè)組件一個(gè)倉庫的,對于一般的組件來說,并沒有什么問題,但是對于有的業(yè)務(wù)線,本身規(guī)模比較大,包含了若干個(gè)子業(yè)務(wù),比如知乎大學(xué),電子書、live 和私家課等子業(yè)務(wù),這些子業(yè)務(wù)本身功能獨(dú)立,但是共享整個(gè)業(yè)務(wù)線的基礎(chǔ)代碼,同時(shí)大業(yè)務(wù)線也有會一些匯總所有子業(yè)務(wù)的頁面,它們的關(guān)系是這個(gè)樣子:
這幾個(gè)業(yè)務(wù)如果都要拆分出去獨(dú)立成組件,然后抽離公共部分成為也成為一個(gè)業(yè)務(wù)線基礎(chǔ)組件,這時(shí)候會面臨一個(gè)很大的問題:由于幾條業(yè)務(wù)線都屬于同一個(gè)主業(yè)務(wù)線,做活動(dòng)或者上新 Feature 的時(shí)候,這幾個(gè)組件經(jīng)常會發(fā)生聯(lián)動(dòng),需要先更新 base 再更新其他業(yè)務(wù)線,提交 mr 也要同時(shí)提多個(gè)倉庫,出現(xiàn)頻繁的連鎖更新;而如果不拆的話,業(yè)務(wù)線代碼本身就已經(jīng)很龐大,即使是單獨(dú)編譯組件 app 也會很慢,并且隨著時(shí)間的推移,各個(gè)業(yè)務(wù)線的代碼邊界會像組件化之前的主工程一樣逐漸劣化,耦合會越來越嚴(yán)重。
所以現(xiàn)在需求變成了這個(gè)樣子:
對外保持只有一個(gè)組件:有聯(lián)動(dòng)需求的時(shí)候,組件仍然只發(fā)布一次更新 各個(gè)子業(yè)務(wù)仍舊保持互相獨(dú)立和隔離,可以獨(dú)立運(yùn)行
我們曾經(jīng)試圖使用 sourceSets 的方式將不同的業(yè)務(wù)代碼放到不同的文件夾,但是 sourceSets 的問題在于,它并不能限制各個(gè) sourceSet 之間互相引用,base 模塊甚至可以直接引用最上層的代碼,雖然可以在編譯期進(jìn)行檢查,但是總有一些后知后覺的意味,并且使用 sourceSets 想讓各個(gè)模塊單獨(dú)跑起來配置也比較麻煩。而 Android Studio 的 module 天然具有隔離的優(yōu)勢。所以我們的解決方案是在組件工程中使用多 Module 結(jié)構(gòu): 自我介紹一下,小編13年上海交大畢業(yè),曾經(jīng)在小公司待過,也去過華為、OPPO等大廠,18年進(jìn)入阿里一直到現(xiàn)在。
深知大多數(shù)初中級Android工程師,想要提升技能,往往是自己摸索成長或者是報(bào)班學(xué)習(xí),但對于培訓(xùn)機(jī)構(gòu)動(dòng)則近萬的學(xué)費(fèi),著實(shí)壓力不小。自己不成體系的自學(xué)效果低效又漫長,而且極易碰到天花板技術(shù)停滯不前!
因此收集整理了一份《2024年Android移動(dòng)開發(fā)全套學(xué)習(xí)資料》,初衷也很簡單,就是希望能夠幫助到想自學(xué)提升又不知道該從何學(xué)起的朋友,同時(shí)減輕大家的負(fù)擔(dān)。
既有適合小白學(xué)習(xí)的零基礎(chǔ)資料,也有適合3年以上經(jīng)驗(yàn)的小伙伴深入學(xué)習(xí)提升的進(jìn)階課程,基本涵蓋了95%以上Android開發(fā)知識點(diǎn),真正體系化!
由于文件比較大,這里只是將部分目錄截圖出來,每個(gè)節(jié)點(diǎn)里面都包含大廠面經(jīng)、學(xué)習(xí)筆記、源碼講義、實(shí)戰(zhàn)項(xiàng)目、講解視頻,并且會持續(xù)更新!
如果你覺得這些內(nèi)容對你有幫助,可以掃碼獲?。。。▊渥ⅲ篈ndroid)
寫在最后
由于本文羅列的知識點(diǎn)是根據(jù)我自身總結(jié)出來的,并且由于本人水平有限,無法全部提及,歡迎大神們能補(bǔ)充~
將來我會對上面的知識點(diǎn)一個(gè)一個(gè)深入學(xué)習(xí),也希望有童鞋跟我一起學(xué)習(xí),一起進(jìn)階。
提升架構(gòu)認(rèn)知不是一蹴而就的,它離不開刻意學(xué)習(xí)和思考。
**這里,筆者分享一份從架構(gòu)哲學(xué)的層面來剖析的視頻及資料分享給大家,**梳理了多年的架構(gòu)經(jīng)驗(yàn),籌備近1個(gè)月最新錄制的,相信這份視頻能給你帶來不一樣的啟發(fā)、收獲。
最近還在整理并復(fù)習(xí)一些Android基礎(chǔ)知識點(diǎn),有問題希望大家夠指出,謝謝。
希望讀到這的您能轉(zhuǎn)發(fā)分享和關(guān)注一下我,以后還會更新技術(shù)干貨,謝謝您的支持!
轉(zhuǎn)發(fā)+點(diǎn)贊+關(guān)注,第一時(shí)間獲取最新知識點(diǎn)
Android架構(gòu)師之路很漫長,一起共勉吧!
《互聯(lián)網(wǎng)大廠面試真題解析、進(jìn)階開發(fā)核心學(xué)習(xí)筆記、全套講解視頻、實(shí)戰(zhàn)項(xiàng)目源碼講義》點(diǎn)擊傳送門即可獲??!
個(gè)月最新錄制的,相信這份視頻能給你帶來不一樣的啟發(fā)、收獲。
[外鏈圖片轉(zhuǎn)存中…(img-JOBdnVJt-1712640764581)]
[外鏈圖片轉(zhuǎn)存中…(img-apvsP5tI-1712640764582)]
最近還在整理并復(fù)習(xí)一些Android基礎(chǔ)知識點(diǎn),有問題希望大家夠指出,謝謝。
希望讀到這的您能轉(zhuǎn)發(fā)分享和關(guān)注一下我,以后還會更新技術(shù)干貨,謝謝您的支持!
轉(zhuǎn)發(fā)+點(diǎn)贊+關(guān)注,第一時(shí)間獲取最新知識點(diǎn)
Android架構(gòu)師之路很漫長,一起共勉吧!
《互聯(lián)網(wǎng)大廠面試真題解析、進(jìn)階開發(fā)核心學(xué)習(xí)筆記、全套講解視頻、實(shí)戰(zhàn)項(xiàng)目源碼講義》點(diǎn)擊傳送門即可獲?。?/p>
柚子快報(bào)激活碼778899分享:GitHub標(biāo)星9
精彩鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。