前幾篇文章中,分析了狹義上的架構(gòu)概念翩剪,如《關(guān)于移動(dòng)架構(gòu)的思考與總結(jié)》, 《關(guān)于移動(dòng)架構(gòu)乳怎,有這一篇就夠了》。從狹義上來(lái)講前弯,Android的架構(gòu)概念就在這兒蚪缀,無(wú)論怎么變秫逝,都是加加減減一些邊邊角角的東西,不足在意询枚。
從本篇開始违帆,打算從廣義上探究一下移動(dòng)架構(gòu)的思想;包括現(xiàn)在仍然比較火熱的模塊化金蜀,組件化前方, 插件化等架構(gòu)思想。話說(shuō)在前面廉油,本篇旨在說(shuō)明當(dāng)前主流架構(gòu)的設(shè)計(jì)概念惠险,讓大家了解當(dāng)前的架構(gòu)形式,以及主要用了什么設(shè)計(jì)思想抒线,什么設(shè)計(jì)思路班巩,算是引導(dǎo)篇...
模塊化,組件化嘶炭,插件化
上述概念已經(jīng)好久了抱慌,或許還是有一些同胞對(duì)這些概念不是很清楚,大體知道是什么眨猎,但是詳細(xì)也不知道是什么∫纸現(xiàn)在來(lái)解析一下。
單工程模式
移動(dòng)開發(fā)誕生睡陪,我們開發(fā)移動(dòng)項(xiàng)目寺渗,我相信大多用的是單工程單任務(wù)的開發(fā)模式,二話不說(shuō)兰迫,直接就開始寫起信殊,是不是這樣呢? new Project -> 分包 -> 寫起汁果。我相信都經(jīng)歷過(guò)涡拘,也寫的比較爽,為什么呢据德? 這種模式不涉及亂七八糟的處理方式鳄乏, 上手快,開發(fā)快棘利,足夠敏捷橱野。那么原因是什么呢?Mobile Project 剛起步赡译,項(xiàng)目都偏小仲吏,一些附加業(yè)務(wù)還沒(méi)綁到App上不铆。
模塊化
Android Studio出來(lái)了蝌焚,多出來(lái)了一個(gè)新的概念裹唆, Project, Module... 模塊;當(dāng)時(shí)以包的形式分離的公共包c(diǎn)ommon,現(xiàn)在成了AS中的Module只洒。大家都知道许帐,Module包含兩種格式: application, library毕谴。也就是說(shuō)成畦,一個(gè)Module就是一個(gè)小的項(xiàng)目,也是AS概念中的模塊涝开。因此我們開始設(shè)計(jì)common模塊循帐, common_business模塊,甚至db模塊舀武。模塊的好處是什么拄养? 相比于包來(lái)講,模塊更靈活银舱,耦合更低瘪匿,隨意插拔,想引入哪個(gè)就引入哪個(gè)寻馏。根據(jù)不同的關(guān)注點(diǎn)棋弥,將一個(gè)項(xiàng)目的可以共享的部分抽取出來(lái),形成獨(dú)立的Module诚欠,就是模塊化顽染。模塊化不只包含公共部分,當(dāng)然也可以是業(yè)務(wù)模塊轰绵。
組件化
平時(shí)看看論壇家乘,好多人都在問(wèn): 模塊化和組件化有什么區(qū)別? 到底有什么區(qū)別呢藏澳,其實(shí)很腥示狻;但并不是完全相同的概念翔悠。 通過(guò)以上模塊化的概念講述业崖,應(yīng)該對(duì)模塊化有了一個(gè)了解,那么區(qū)別是什么呢蓄愁?
組件化是建立在模塊化思想上的一次演進(jìn)双炕,一個(gè)變種。組件化本來(lái)就是模塊化的概念撮抓。但是組件化的核心是
什么妇斤? 是模塊角色的可轉(zhuǎn)換性。是的,就是可轉(zhuǎn)換性站超。
組件化的核心是角色的轉(zhuǎn)換荸恕。 在打包時(shí), 是library; 在調(diào)試時(shí)死相, 是application融求。
怎么理解組件化的概念 ?
Module的模式分兩種算撮, application和library生宛。 library就是引用庫(kù),如你抽取的common肮柜。 application就是一個(gè)apk陷舅, 是一個(gè)完整的項(xiàng)目。
在調(diào)試時(shí)审洞,我只關(guān)心我負(fù)責(zé)的模塊蔑赘,我希望我的模塊是一個(gè)單獨(dú)的app,因?yàn)檫@樣更小,業(yè)務(wù)更專一预明,相對(duì)來(lái)講修改與調(diào)試就會(huì)越省時(shí)省心缩赛,編譯就會(huì)越快。試想當(dāng)你需要改一段代碼撰糠,既要關(guān)注自己的酥馍,也要關(guān)注別人的,是一種什么體驗(yàn) 阅酪? 或者旨袒, 編譯一個(gè)項(xiàng)目10M的代碼和一個(gè)工程全部1G的代碼,哪個(gè)比較舒服一些术辐?
插件化
又有人問(wèn)了: 插件化和組件化又有什么區(qū)別呢砚尽?插件化嚴(yán)格意義來(lái)講,其實(shí)也算是模塊化的觀念辉词。將一個(gè)完整的工程必孤,按業(yè)務(wù)劃分為不同的插件,都是分治法的一種體現(xiàn)瑞躺》筇拢化整為零,相互配合幢哨。赡勘,越小的模塊越容易維護(hù)。 插件化按理也算是模塊化的一種體現(xiàn)捞镰,和組件化就不一個(gè)概念了闸与。那么毙替,到底有什么區(qū)別呢?
組件化的單位是組件(module)践樱。
插件化的單位是apk(一個(gè)完整的應(yīng)用)厂画。
組件化實(shí)現(xiàn)的是解耦與加快編譯, 隔離不需要關(guān)注的部分映胁。
插件化實(shí)現(xiàn)的也是解耦與加快編譯,同時(shí)實(shí)現(xiàn)熱插拔也就是熱更新甲雅。
組件化的靈活性在于按加載時(shí)機(jī)切換解孙,分離出獨(dú)立的業(yè)務(wù)組件,比如微信的朋友圈
插件化的靈活性在于是加載apk, 完全可以動(dòng)態(tài)下載抛人,動(dòng)態(tài)更新弛姜,比組件化更靈活。
組件化能做的只是妖枚, 朋友圈已經(jīng)有了廷臼,我想單獨(dú)調(diào)試,維護(hù)绝页,和別人不耦合荠商。但是和整個(gè)項(xiàng)目還是有關(guān)聯(lián)的。
插件化可以說(shuō)朋友圈就是一個(gè)app, 我需要整合了续誉,把它整合進(jìn)微信這個(gè)大的app里面
其實(shí)從框架名稱就可以看出: 組 和 插莱没。
組本來(lái)就是一個(gè)系統(tǒng),你把微信分為朋友圈酷鸦,聊天饰躲, 通訊錄按意義上劃為獨(dú)立模塊,但并不是真正意義上的獨(dú)立模塊臼隔。
插本來(lái)就是不同的apk嘹裂, 你把微信的朋友圈,聊天摔握,通訊錄單獨(dú)做一個(gè)完全獨(dú)立的app, 需要微信的時(shí)候插在一起寄狼,就是一個(gè)大型的app了。
插件化的加載是動(dòng)態(tài)的氨淌,這點(diǎn)很重要例嘱,也是靈活的根源。
以上是對(duì)三個(gè)思想的解析宁舰,相信應(yīng)該能明白不同的概念的具體意義和區(qū)別在哪了拼卵。在《關(guān)于移動(dòng)架構(gòu)的思考與總結(jié)》中我指出,所謂架構(gòu)蛮艰,無(wú)非兩個(gè)方面: 分層和通信方式腋腮。 其實(shí)廣義的架構(gòu)也可以說(shuō)是這兩個(gè)方面:子模塊(子系統(tǒng))劃分和通信。
子模塊劃分
除了大家公認(rèn)的common部分, 業(yè)務(wù)模塊的劃分尤為重要即寡,相比于狹義上的架構(gòu)徊哑,廣義上的子系統(tǒng)的劃分的關(guān)注點(diǎn),很考驗(yàn)技術(shù)經(jīng)驗(yàn)以及對(duì)業(yè)務(wù)的理解聪富。
通信方式
模塊化的通信方式莺丑,無(wú)非是相互引入;我抽取了common, 其他模塊使用自然要引入這個(gè)module
組件化的通信方式墩蔓,按理說(shuō)可以劃分為多種梢莽,主流的是隱式和路由。隱式的存在使解耦與靈活大大降低奸披,因此路由是主流
插件化的通信方式昏名,不同插件本身就是不同的進(jìn)程了。因此通信方式偏向于Binder機(jī)制類似的進(jìn)程間通信
廢話說(shuō)了這么多阵面,其實(shí)本篇作為組件化的引導(dǎo)篇轻局,本意是要探究一下組件化的思路的,嗯样刷,本篇只講思路仑扑;其實(shí)思路清晰了,結(jié)合一定的技術(shù)儲(chǔ)備置鼻,完全可以自己來(lái)實(shí)現(xiàn)夫壁。好了,開始切入主題...
組件化的技術(shù)準(zhǔn)備
反射與apt
gradle與groovy
路由機(jī)制
情報(bào)篇
做一件事沃疮,首先要明白我們要做什么盒让, 然后劃分步驟,哪一步怎么做司蔬,最后逐個(gè)解決邑茄。這也是分治法的一種思維方式,它當(dāng)然不只是一種算法的解決思想俊啼。只有這樣肺缕,我們才會(huì)建立信息,不會(huì)一下子被嚇傻從而放棄授帕。
組件化的思想是Module模式的切換同木。 上面已經(jīng)說(shuō)過(guò),在打包時(shí),業(yè)務(wù)module為library; 調(diào)試時(shí)跛十,業(yè)務(wù)module成了application彤路。
1.如何切換module的模式呢 ?
我相信都能想到芥映,定義一個(gè)Boolean變量作為開關(guān)酸钦。根據(jù)開關(guān)分別設(shè)置module的模式,如下
if (isModule) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
2.兩種模式的區(qū)別是什么岸夯?
1. applicationId (包的配置)
只存在于apply plugin: 'com.android.application'模式下
2. Manifest.xml(主頁(yè)面的配置)
集成模式下,使用app模塊下的Manifest.xml配置径筏; 組件模式下,使用組件自己的Manifest.xml配置
3. Application
不同的組件肯定有自己的初始化的資源或框架,因此自定義的Application也是必要的。但是集成模式下棺滞,會(huì)造成重復(fù)的Application
3. 面臨的問(wèn)題
問(wèn)題1:多業(yè)務(wù)模塊下的統(tǒng)一配置
問(wèn)題2:Application分發(fā)
問(wèn)題3:資源的沖突
注意:不同的業(yè)務(wù)模塊禁止彼此依賴
解決方案1:
不同的模塊(20個(gè)業(yè)務(wù)模塊)的配置,必須做到統(tǒng)一配置矢渊。在Java代碼實(shí)現(xiàn)統(tǒng)一配置继准,SO Easy ~ 但是在gradle中呢 ? 那就是定義一個(gè)配置文件昆淡,統(tǒng)一存放需要配置的項(xiàng)锰瘸。如下
ext {
isModule = false // 組件開關(guān): true 組件 false 集成
defaultConfig = [
minSdkVersion : 14,
targetSdkVersion : 26,
versionCode : 1,
versionName : "1.0",
testInstrumentationRunner: "android.support.test.runner.AndroidJUnitRunner"
]
android = [
compileSdkVersion: 26
]
applicationId = [
app : "com.archer.componentsarchitecture",
card1: "com.archer.card1",
card2: "com.archer.card2"
]
supportLibrary = "26.1.0"
appcompatv7a = "com.android.support:appcompat-v7:${supportLibrary}"
resourcePrefixs =[
card1: "card1",
card2: "card2"
]
router = [
arouter: "com.alibaba:arouter-api:1.2.1.1",
processor: "com.alibaba:arouter-compiler:1.1.2.1"
]
}
在Project下創(chuàng)建一個(gè)config.gradle(什么刽严?創(chuàng)建不了昂灵?那就把Project自帶的build.gradle復(fù)制一份rename & clear)。 ext是groovy提供的擴(kuò)展參數(shù)舞萄,不可修改的眨补。 以下可以隨意定義自己的配置,如代碼倒脓。這里說(shuō)下兩個(gè)概念:
- 占位符 ${supportLibrary} 占據(jù)一個(gè)位置撑螺,然后用{}里面的變量補(bǔ)充,達(dá)到一致配置的目的
- android = [
compileSdkVersion: 26
]
以上相當(dāng)于定義了一個(gè)Map, 存放鍵值對(duì)崎弃,以Key: Value的形式甘晤,以,分隔饲做。這是groovy的寫法线婚。android 為Map的名稱,你可以用你自己的命名盆均,但是注意不要和系統(tǒng)變量沖突
以上是統(tǒng)一變量的定義塞弊,配置文件config.gradle。 配置文件定義好了泪姨,那么如何引入呢游沿?
-
在[Project]下的build.gradle引入配置文件
image.png
2.在Module中引用是通過(guò)rootProject.ext.你定義的名稱。但是每次這么用比較繁瑣肮砾,推薦定義變量實(shí)現(xiàn)诀黍,如下
if (isModule) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
def cfg = rootProject.ext.defaultConfig
def drd = rootProject.ext.android
def app = rootProject.ext.applicationId
android {
compileSdkVersion drd.compileSdkVersion
defaultConfig {
if (isModule) {
applicationId app['card2']
}
minSdkVersion cfg.minSdkVersion
targetSdkVersion cfg.targetSdkVersion
versionCode cfg.versionCode
versionName cfg.versionName
testInstrumentationRunner cfg.testInstrumentationRunner
resourcePrefix rootProject.ext.resourcePrefixs['card2']
javaCompileOptions {
annotationProcessorOptions {
arguments = [ moduleName : project.getName() ]
}
}
}
調(diào)用Groovy map中的字段的兩種方式: rootProject.ext.android.key和 rootProject.ext.android['key']
解決方案2:
application的分發(fā),錯(cuò)誤的做法是不同的組件下初始化自己的框架仗处,工具等蔗草。正確的做法是在BaseApplication或統(tǒng)一實(shí)現(xiàn)公共模塊如網(wǎng)絡(luò)咒彤, 緩存, 數(shù)據(jù)庫(kù)等的初始化咒精,在各Module實(shí)現(xiàn)自己需要的初始化镶柱,來(lái)避免重復(fù)的初始化與沖突。
解決方案3:
資源的沖突解決辦法有兩個(gè):
1) 公共資源建議由公共模塊管理
2) 模塊私有資源模叙,添加前綴限制 (只能解決xml沖突)
3)資源謹(jǐn)慎命名
資源命名只能在開發(fā)中加以注意歇拆, 通過(guò)以上共有資源和前綴極大可能的保證資源不會(huì)沖突,且不會(huì)重復(fù)浪費(fèi)范咨。至于萬(wàn)一的沖突故觅,只能交給開發(fā)規(guī)范了。
解決方案4:
這個(gè)是新加的渠啊,也就是前面說(shuō)的输吏,怎么控制application和library的轉(zhuǎn)換,全部配置如下:
// 自由控制模式轉(zhuǎn)換
if (isModule) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
// 包名配置
defaultConfig {
if (isModule) {
applicationId app['card2']
}
}
// Manifest.xml application配置
sourceSets {
main {
if (isModule) {
// src/main下新建文件夾替蛉,存放組件模式下的Manifest.xml與Application
manifest.srcFile 'src/main/component/AndroidManifest.xml'
java.srcDirs = ['src/main/java', 'src/main/component/java']
} else {
// library模式下不需要Application
manifest.srcFile 'src/main/AndroidManifest.xml'
java.srcDirs 'src/main/java'
}
}
}
以上配置在相應(yīng)Module中的build.gradle的android下
本來(lái)打算出這篇為Router原理思想的贯溅,結(jié)果為了引導(dǎo)從模塊化的概念直到組件化的核心概念以及初步實(shí)現(xiàn)。因?yàn)檎娴牟簧偃丝赡軐?duì)這些概念了解不是很清楚躲查,包括以前的我它浅,比如模塊化和組件化。從整篇來(lái)看镣煮,組件化無(wú)非就是實(shí)現(xiàn)了一次轉(zhuǎn)換姐霍,解決的一些轉(zhuǎn)換過(guò)程中涉及的問(wèn)題。沒(méi)那么難典唇, 也存在一些坑镊折,只有在開發(fā)過(guò)程中隨著遇到進(jìn)一步填平。組件化的配置核心就是library和application的toggle介衔。 真正實(shí)現(xiàn)的功能核心卻是通信部分的路由實(shí)現(xiàn)部分恨胚,下一篇講一下,如何手寫Rooter通信框架夜牡。