進階高工必備技能:Android元件化架構全解析!(附視訊+電子書+學習筆記分享)
前言
現在大多數的App都會在重構的時候想到元件化或者說模組化,特別是在大廠或者一些有規模的公司顯得尤為重要,目的是為了方便App解耦和優化。在我的理解裡面元件化即將每個功能相同的模組一個個的裝起來,然後以library的形式供我們的主app模組呼叫,而在主app模組中不會去進行任何的業務邏輯,只管打包好了,而除了主app模組,其他模組各回各家,各找各媽,幹自己的業務邏輯去,簡單的說元件化就是讓library和application之間可以互相切換,libray可以作為單獨的App執行也可以作為依賴庫給主app呼叫。這種構建思想能大大的提升開發效率,減低程式碼的維護成本,減少程式碼的耦合度,讓他人簡單易懂。
整體結構
- common:基礎元件部分,與業務無關,需要所有元件共同依賴的部分,如:網路請求封裝、圖片載入封裝、ui相關基類、工具集合等(當然這些內容可以依據分層原則放在不同的基礎module中)
- router-comp:路由驅動元件,承載整個專案的路由工作
- comp1:業務元件1,如視訊元件,可獨立執行
- comp2:業務元件2,如新聞元件,可獨立執行
- comp3:業務元件3,如視訊元件,可獨立執行
- app:殼工程,用於將各個元件組裝成一個完成app
元件化所面臨的問題
- 整合模式與元件模式轉換(熱插拔)
- 元件之間頁面跳轉(路由)
- 元件之間通訊、呼叫彼此服務
- 打包混淆
元件化的實現
針對上面所說的幾個問題,下面我們逐個說明它們的解決方案,當解決完這些問題,你會發現,你已經搭建了一個基於元件化的專案。下圖是一個完整的元件化專案結構:common是基礎元件module,作為library存在,需要所有元件依賴;comp1、comp2作為元件存在,可配置成library或可獨立執行的module;app是個殼,通過組裝元件實現其價值。
整合模式與元件模式轉換(熱插拔)
Android工程通過gradle構建,通過配置每個module的gradle,來實現module的不同表現。Android Studio的module有兩種屬性,分別是:
- application屬性:可獨立執行,也就是我們的app
- library屬性:不可獨立執行,被app依賴的庫
module屬性通過其目錄下的gradle檔案配置,當module屬性為application時,該module作為完整的app存在,可以獨自執行,方便編譯和除錯;當module屬性為library時,該module作為一個依賴庫,被殼工程依賴並組裝成一個app。那麼如何讓這兩種模式可以自動轉換呢?如果每次切換模式的時候,都手動去修改每個元件的配置,元件少的情況下還可以接受,元件多了會非常不方便,下面就讓我們來聊聊如何實現兩種模式的自動轉換。1、首先,宣告全域性配置變數,來標識module的屬性(app or library),如在工程目錄下的build.gradle檔案中宣告布林變數ext.isModule,true代表元件作為可獨立執行的app,false代表元件作為被依賴的library,如下所示
buildscript {
ext.kotlin_version = '1.3.21'
ext.isModule = true //true-每個元件都是單獨的module,可獨立執行 false-元件作為library存在
repositories {
google()
jcenter()
}
}
2、配置元件的build.gradle檔案
//1 if (rootProject.ext.isModule) {
//可獨立執行的app
apply plugin: 'com.android.application'
} else{
//被依賴的library
apply plugin: 'com.android.library'
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 28
defaultConfig {
//applicationId "com.study.comp1" //2 如果沒有,預設包名為applicationId
minSdkVersion 19
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
//3
sourceSets {
main {
if(rootProject.ext.isModule){
manifest.srcFile 'src/main/java/module/AndroidManifest.xml'
} else{
manifest.srcFile 'src/main/java/library/AndroidManifest.xml'
java {//移除module包下的程式碼
exclude 'module'
}
}
}
}
}
上面是擷取的元件gradle的部分程式碼,包含了元件化需要配置的所有內容,每一點都進行了註釋
- 註釋1:如上所述,根據isModule的值,來設定module的屬性,作為app or library
- 註釋2:當module屬性為library時,不能設定applicationId;當為app時,如果未設定applicationId,預設包名為applicationId,所以為了方便,此處不設定applicationId
- 註釋3:Android Studio會為每個module生成對應的AndroidManifest.xml檔案,宣告自身需要的許可權、四大元件、資料等內容;當module屬性為app時,其對應的AndroidManifest.xml需要具備完整app所需要的所有配置,尤其是宣告Application和launch的Activity;當module屬性為library時,如果每個元件都宣告自己的Application和launch的Activity,那在合併的時候就會發生衝突,編譯也不會通過,所以,就需要為當前module重新定義一個AndroidManifest.xml檔案,不宣告Application和launch的Activity,然後根據isModule的值指定AndroidManifest.xml的路徑,因此就有了註釋3處的寫法。為了避免整合模式下的命名衝突,每個檔案都以自身module名為字首來命名會是一個很好的方法。下圖是該module的目錄
在需要切換module屬性的時候改變步驟1處宣告的變數值,然後重新編譯即可
元件之間頁面跳轉(路由)
在元件化架構中,不同的元件之間是平衡的,不存在相互依賴的關係(可參考文章開頭的架構圖)。因此,假設在元件A中,想要跳轉到元件B中的頁面,如果使用Intent顯式跳轉就行不通了,而且大家都知道,Intent隱式跳轉管理起來非常不方便,所以Arouter出現了,並且有強大的技術團隊支援,可以放心使用了。那麼如何在元件化架構中應用Arouter呢?下面詳細來聊一聊
1、依賴處理
在common元件中將Arouter依賴進來,並配置編譯引數;在業務元件中引入arouter編譯器外掛,同時配置編譯器引數,下面是Common元件gradle檔案的部分片段
//配置arouter編譯器引數,每個元件都需配置 kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
dependencies {
//arouter api,只需在common元件中引入一次
api('com.alibaba:arouter-api:1.4.1') {
exclude group: 'com.android.support'
}
//arouter編譯器外掛,每個元件都需引入
kapt 'com.alibaba:arouter-compiler:1.2.2'
}
2、初始化及編碼實現
在元件架構中,經常會遇到元件需要使用全域性Context的情況,當元件屬性為app時,可以通過自定義Application實現;當元件屬性為library時,由於元件被app依賴,導致無法呼叫app的Application例項,而且自身不存在Application;所以,這裡給出的方案是在common元件中建立一個BaseApplication,然後讓整合模式(元件模式)下的Application繼承BaseApplication,在BaseApplication中獲取全域性Context,並做一些初始化的工作,這裡需要初始化Arouter,如下是在common元件中宣告的BaseApplication。
abstract class BaseApplication : Application() {
companion object {
var _context: Application? = null
//獲取全域性Context
fun getContext(): Application {
return _context!!
}
}
override fun onCreate() {
super.onCreate()
_context = this
//初始化Arouter
initARouter()
//初始化其他第三方庫
}
private fun initARouter() {
if (BuildConfig.DEBUG) {
ARouter.openDebug()
ARouter.openLog()
}
ARouter.init(this)
}
override fun onTerminate() {
super.onTerminate()
//清理Arouter登錄檔
ARouter.getInstance().destroy()
}
}
根據Arouter的路由特性,初始化之後,就可以通過@Route註解註冊頁面,然後呼叫Arouter api實現頁面的跳轉了(這裡所謂的跨元件頁面跳轉是指在整合模式下,而非元件模式下),無關乎是否在同一個元件下面,假設我們要從元件1頁面攜帶引數跳轉到元件2頁面,請看下面示例
/**
* 在元件2中通過@Route註解註冊該頁面
*/ @Route(path = "/comp2/msg",name = "我是元件2的MSGActivity")
class Comp2MsgActivity : BaseActivity() {
//傳遞過來的引數
@Autowired(name = "msg")
@JvmField
var msg: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//注入傳遞的引數
ARouter.getInstance().inject(this)
setContentView(R.layout.comp2_activity_msg)
comp2_msg_msg.text = msg!!
}
}
//在元件1中發起跳轉命令
ARouter.getInstance()
.build("/comp2/msg")
.withString("msg", "hello Im from Comp1")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.navigation()
以上便完成了一次簡單的跨越元件的頁面跳轉,僅僅是Arouter的基本使用而已。解決了元件間頁面跳轉的問題後,我們來看看元件之間通訊、呼叫彼此服務的實現。元件之間通訊、呼叫彼此服務元件間通訊功能和路由功能有著共通的地方,即都是利用Arouter的基礎功能實現,在Arouter驅動層定義各個元件對外提供的介面,然後在元件自身模組實現該介面,通過Arouter呼叫其他元件服務。假設我們在元件2中需要呼叫元件1中的服務,可以總結為以下3點
- 定義介面:在common元件中定義元件1對外提供的介面CompServer1,注意該介面型別為Arouter模板型別IProvider
/**
* 元件1對外提供的介面
*/
interface CompServer1 : IProvider {
fun showMsg(msg: String)
}
- 實現介面:在comm1中實現上面定義的介面,並通過@Route註解註冊
@Route(path = "/comp1/server",name = "comp1對外提供的服務")
class CompServer : CompServer1 {
var mContext: Context? = null
override fun showMsg(msg: String) {
Toast.makeText(mContext,msg,Toast.LENGTH_SHORT).show()
}
override fun init(context: Context?) {
this.mContext = context!!
}
}
- 呼叫服務:在完成元件1介面的定義和實現之後,在元件2中需要的地方呼叫該介面即可
val server1 = ARouter.getInstance().build("/comp1/server").navigation() as CompServer1
server1.showMsg("我從comp2吊起了comp1的介面")
有沒有感覺很簡單??沒錯,就是這麼簡單,趕緊去用吧!哈哈
打包混淆
說到混淆,有人可能會疑惑,如果在各個元件中混淆可不可以?不建議這樣混淆!!因為元件在整合模式下被gradle構建成了release型別的aar包,如果在元件中進行混淆,一旦程式碼出現了bug,這個時候就很難根據日誌去追蹤bug產生的原因,而且不同元件分別進行混淆非常不方便維護和修改,這也是不推薦在業務元件中配置buildType(構建型別)的原因。所以,元件化專案的程式碼混淆放在整合模式下的app殼工程,各個業務元件不配置混淆。整合模式下在app殼工程.gradle檔案的release構建模式下開啟混淆,其他buildType配置和普通專案相同,混淆檔案放在app殼工程下,各個元件的程式碼混淆均放在該混淆檔案中。
最後
以上,我們已經逐一解決了元件化所面對的各個問題,至此,我們已經搭建了一個簡單的元件化架構的專案,是不是感覺在不知不覺中就實現了,並不是很難哦!現在,我來總結一下元件化的優勢了。
-
解耦:將業務元件程式碼90%與工程解耦,只所以是90%而非100%,是因為業務元件需要在common元件中宣告對外開放的介面,那有沒有什麼方式可以做到完全解耦呢?目前還沒有發現更好的辦法。。。
-
提高開發效率:依賴解耦這一優勢,團隊成員可以只專注於自己負責的元件,開發效率更高;而且,元件開發過程中只需編譯自身的module,這樣大大縮短了編譯時長,避免了漫長的等待編譯局面。
-
結構清晰:在業務元件明確拆分的前提下,專案結構變的異常清晰,非常方便全域性掌控。
為了方便大家更加深入學習元件化架構原理及底層原始碼解讀,我這邊收集整理了一套視訊+電子書+學習筆記的元件化學習資料。視訊是由愛奇藝高工Lance老師主講的《從零開始學元件化》以及元件化學習電子書《Android元件化架構》,和一套《第三方開源框架解析學習筆記》,包含外掛化,元件化,熱修復等熱門第三方框架的解析學習筆記!
需要以上元件化學習資料的小夥伴,可以在點贊+任意評論後,點選此處免費獲取!
需要以上元件化學習資料的小夥伴,可以在點贊+任意評論後,點選此處免費獲取!
相關文章
- 未雨綢繆:Java高階架構進階必學之⑥大知識要點附視訊學習資料Java架構
- Android 學習筆記架構篇Android筆記架構
- 學習資料分享——java視訊教程及電子書免費下載Java
- 架構必備技能第一談架構
- Android進階必學retrofit原始碼解析Android原始碼
- 鴻洋大神的安卓學習推薦書單!入門到進階,架構師學習分享!安卓架構
- 架構學習筆記架構筆記
- Go 進階學習筆記Go筆記
- Swift進階學習筆記Swift筆記
- Git進階學習筆記Git筆記
- 【Java高階必備】如何優化Spring Cloud微服務註冊中心架構?【石杉的架構筆記】Java優化SpringCloud微服務架構筆記
- Java架構師必備技能:docker使用大全Java架構Docker
- 《音視訊開發進階指南》讀書筆記(一) —— 音視訊基礎概念筆記
- Java進階必備:優雅的告訴面試官訊息中介軟體該如何實現高可用架構?【石杉的架構筆記】Java面試架構筆記
- JavaMail學習筆記(四)、使用POP3協議接收並解析電子郵件(全)JavaAI筆記協議
- 元件化學習筆記 一元件化筆記
- Vue 進階必學之高階元件 HOC(保姆式教學,衝擊20k必備)Vue元件
- “平板電視”學習筆記筆記
- Java高階架構專案實戰(含面電子書)Java架構
- 架構學習筆記系列一架構筆記
- 架構學習筆記系列三架構筆記
- 架構學習筆記系列二架構筆記
- Adaptive AUTOSAR 學習筆記 5 - 架構 - 物理檢視APT筆記架構
- “React元件間通訊”學習筆記React元件筆記
- 音視訊學習筆記(一)筆記
- 分享電子書
- python進階學習筆記(一)Python筆記
- Koa2進階學習筆記筆記
- 線段樹進階 學習筆記筆記
- 超全!短視訊製作必備工具
- 程式設計師學習必備書單彙總,超全!程式設計師
- Android開發者必備的技能你會嗎?MVVM 最新學習心得!AndroidMVVM
- 阿里P8架構師進階心得:分散式資料庫架構MyCat學習筆記送給你阿里架構分散式資料庫筆記
- 職場必備技能-BI資料視覺化,後悔沒早學視覺化
- 學習jQuery的免費資源:電子書、視訊、教程和部落格jQuery
- 聯邦學習(電子工業出版社)——讀書筆記(3)聯邦學習筆記
- Adaptive AUTOSAR 學習筆記 4 - 架構 - 邏輯檢視APT筆記架構
- JavaScript 記憶體機制(前端同學進階必備)JavaScript記憶體前端