今天這個(gè)題目很難取,組件化范疇的內(nèi)容,又是EventBus的相關(guān)內(nèi)容,但是通過(guò)gradle插件化的形式來(lái)做,簡(jiǎn)單就說(shuō)成組件化下EventBus的消息類型自動(dòng)編譯,有更好主意的小伙伴可以推薦。先說(shuō)下背景,組件化過(guò)程中肯定涉及到數(shù)據(jù)的傳遞問(wèn)題,EventBus這個(gè)庫(kù)是比較流行的方式,用過(guò)EventBus的小伙伴都知道需要先定義一個(gè)消息類型。在組件化工程里面一般都會(huì)把消息類型定義到公共模塊,這樣跨組件才能傳遞。這樣做能實(shí)現(xiàn)目的,但是有兩個(gè)弊端:
- 需要手動(dòng)去更改公共庫(kù),這就給公共庫(kù)帶來(lái)一定的風(fēng)險(xiǎn),越來(lái)越多的代碼往公共庫(kù)中添加會(huì)造成后期很難維護(hù),組件化的邊界難于界定,公共庫(kù)需要保持穩(wěn)定
- 各組件之間的拆分也不是很干凈,如果修改組件通信內(nèi)容的同時(shí)需要修改公共庫(kù)中的內(nèi)容,如果組件模塊A的開(kāi)發(fā)看不到公共庫(kù)中源碼呢?就捉瞎了
所以我們的目的是,組件需要接受或者發(fā)送的消息類型直接在自己組件范圍內(nèi)定義,不需要去修改組件外的任何代碼,修改越少,bug就越少,就能越早下班不是嗎?
1.結(jié)果
先看下整個(gè)項(xiàng)目結(jié)構(gòu)

分成app/event/modulea/moduleb四個(gè)部分
其中app中發(fā)送消息,a和b可以接收到,a和b中的內(nèi)容大同小異,就只看下a中的代碼,一個(gè)
ADisplay用于顯示接收到的消息,一個(gè)ModuleaMsg用于定義消息類型。

看下 ADisplay中的代碼,要接受的是ModuleaMsg消息:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(ModuleaMsg msg) {
System.out.println("Module A received msg = " + msg.msg);
EventBus.getDefault().unregister(this);
}
ModuleaMsg就是消息類型?在a組件中登記,寫(xiě)一個(gè)以.event結(jié)尾的文件,在里面寫(xiě)入代碼就可

這樣就完成了組件需要完成的全部工作了,完全不需要修改其他的組件。
在發(fā)送的地方,我們這里直接在app組件中發(fā)送消息,
ModuleaMsg moduleaMsg = new ModuleaMsg();
moduleaMsg.msg = "send from app";
EventBus.getDefault().post(moduleaMsg);
ModulebMsg modulebMsg = new ModulebMsg();
modulebMsg.num = 10086;
EventBus.getDefault().post(modulebMsg);
modulea和moduleb接收到消息:
Module A received msg = send from app
Module B received msg = 10086
上面就是全部的使用方式了,如果還有興趣的小伙伴接著往下看實(shí)現(xiàn)原理了。
2.實(shí)現(xiàn)方式
其實(shí)就是通過(guò)gradle插件的方式實(shí)現(xiàn),需要了解怎么定義gradle插件的可以參考我之前的一步步自定義Gradle插件,這里就不再贅述,我們直接看下插件里面的代碼, 主要任務(wù)都在project.afterEvaluate中完成,
project.afterEvaluate {
def eventModule = project.rootProject.childProjects.get(project.EventPluginExt.eventModuleName)
if (eventModule == project) {
throw new GradleException('event module should not be itself!')
}
if (eventModule == null || !eventModule.projectDir.exists()) {
throw new GradleException('event module is not exists')
}
def cleanEventTask = project.task("eventClean", type: Delete) {
delete("${project.rootProject.projectDir}/${eventModule.name}/src/main/java")
}
project.tasks.getByName("clean").dependsOn cleanEventTask
...
}
首先判斷工程中是否有project.EventPluginExt.eventModuleName (= event)這個(gè)模塊,在第一張圖中可以看到有個(gè)event這個(gè)模塊,里面包含了所有的消息類型

然后定義一個(gè)清除組件下舊的.event后綴的文件,優(yōu)先于gradle cleantask。
接著往下看,接著會(huì)拿到所有構(gòu)件體,分成兩種,lib模塊或者application模塊
getVariants().all { variant ->
def variantName = Utils.captureName(variant.name)
System.out.println("variantName is " + variantName)
if (isLibrary()) {
} else {
}
}
private boolean isLibrary() {
return project.android.hasProperty('libraryVariants')
}
先看下lib模塊下面的代碼,分成三部分,先看下第一部分:
def intoAsset
def assetDir = Utils.getOneAssetDir(variant)
if (assetDir == null) {
intoAsset = "${project.projectDir}/src/main/assets/$eventFolder"
} else {
intoAsset = assetDir.absolutePath + "/$eventFolder"
}
def intoEventSdk = "${project.rootProject.projectDir}/${eventModule.name}/src/main/java"
List list = new ArrayList()
variant.sourceSets.each { sourceSet ->
sourceSet.java.srcDirs.each { file ->
list.add(file)
}
}
def javaSrcList = list as String[]
首先拿到lib里面的assets目錄,是為了后面把所有l(wèi)ib中的.event文件都保存到apk中的assets目錄中的eventFolder文件夾;然后定義根目錄下event組件下的文件位置,最后拿到lib下的所有代碼文件。
接著看下第二部分,主要是定義任務(wù)task:
eventLibCopyTask是拷貝任務(wù),把前面拿到的javaSrcList中.event后綴的文件復(fù)制到event模塊下,并且修改后綴名為.java, 在執(zhí)行preBuild前生成- 拿到build過(guò)程的第一個(gè)
task---preBuild- 定義拷貝任務(wù)
eventAssetsCopyTask,負(fù)責(zé)負(fù)責(zé)src/javasrc//.event->assets/cocoFolder//.event- 獲取generateAssets任務(wù)
- 獲取mergeAssets任務(wù)
- 獲取當(dāng)前variant編譯任務(wù)——如assembleDebug/assembleRease
- 定義刪除任務(wù)-刪除的是lib下assets中的delete assets/cocoFolder//.event文件
Task eventLibCopyTask = project.task("eventLibCopyTaskFor" + variantName, type: Copy) {
includeEmptyDirs = false
from javaSrcList
into intoEventSdk
include "**/*.${project.EventPluginExt.postFix}"
rename "(.*)${project.EventPluginExt.postFix}", '$1java'
}
Task preBuildTask = project.tasks.getByName("pre" + variantName + "Build")
Task eventAssetsCopyTask = project.task("eventAssetsCopyTaskFor" + variantName, type: Copy) {
includeEmptyDirs = false
from javaSrcList
into intoAsset
include "**/*.${project.EventPluginExt.postFix}"
}
Task generateAssetsTask = project.tasks.getByName("generate" + variantName + "Assets")
Task mergeAssetsTask = variant.mergeAssets
Task assembleTask = variant.assemble
Task eventAssetsDeleteTask = project.task("eventAssetsDeleteFor" + variantName, type: Delete) {
delete intoAsset
}
第三部分就是定義上面幾個(gè)任務(wù)的執(zhí)行順序,第一步肯定是要拷貝.event文件到event模塊下面,幾個(gè)任務(wù)順序:
1) 拷貝lib中event到event中
2) preBuild
3) 拷貝lib中event到assets中
4) generateAssets
5) mergeAssets
6) 刪除assets中的event文件
7) assemble
preBuildTask.dependsOn eventLibCopyTask
generateAssetsTask.dependsOn eventAssetsCopyTask
mergeAssetsTask.finalizedBy eventAssetsDeleteTask
assembleTask.finalizedBy eventAssetsDeleteTask
// 添加event的預(yù)編譯依賴,避免event模塊在編譯完成之后再執(zhí)行當(dāng)前project的event拷貝任務(wù),導(dǎo)致類查找不到的問(wèn)題
def preEventBuildTask = null
try{
preEventBuildTask = eventModule.tasks.getByName("preBuild")
} catch (Exception ignored) {
}
if(preEventBuildTask != null) {
preEventBuildTask.mustRunAfter eventLibCopyTask
}
上面就是lib模塊的代碼邏輯,接下來(lái)看下application模塊的相關(guān)邏輯,application模塊會(huì)有依賴的aar,aar里面可能有.event的文件,所以需要從依賴中循環(huán)拿到這些文件拷貝到event模塊中
分成兩個(gè)步驟,先看下第一個(gè)步驟,就是從aar依賴中把.event后綴的文件移動(dòng)到項(xiàng)目目錄的event 模塊中
Task appUnpackTask = null
if (variant.hasProperty("compileConfiguration")) {
// For Android Gradle plugin >= 2.5
Attribute artifactType = Attribute.of("artifactType", String)
FileCollection classPathConfig = variant.compileConfiguration.incoming.artifactView {
attributes {
it.attribute(artifactType, "aar")
}
}.files
System.out.println("classPathConfig = " + classPathConfig)
appUnpackTask = project.task("eventAppUnpack" + variantName, type: Copy) {
includeEmptyDirs = false
classPathConfig.each {
from(project.zipTree(it))
}
into "${project.rootProject.projectDir}/${eventModule.name}/src/main/java"
include "**/$eventFolder/**/*.${project.EventPluginExt.postFix}"
rename "(.*)${project.EventPluginExt.postFix}", '$1java'
eachFile {
it.path = it.path.replaceFirst(".*$eventFolder", '')
}
}
}
第二個(gè)步驟就是把a(bǔ)pp模塊下的后綴文件拷貝到event 模塊中,邏輯和前面lib模塊的邏輯一樣的,實(shí)現(xiàn)效果就是javasrc/*.event->event/src/javasrc/*/*.java就不重復(fù)介紹了
List list = new ArrayList()
variant.sourceSets.each { sourceSet ->
sourceSet.java.srcDirs.each { file ->
list.add(file)
}
}
def javaSrcList = list as String[]
System.out.println("javaSrcList is " + javaSrcList)
def intoEventSdk = "${project.rootProject.projectDir}/${eventModule.name}/src/main/java"
Task eventAppCopyTask = project.task("eventAppCopyTaskFor" + variantName, type: Copy) {
includeEmptyDirs = false
from javaSrcList
into intoEventSdk
include "**/*.${project.EventPluginExt.postFix}"
rename "(.*)${project.EventPluginExt.postFix}", '$1java'
}
if (appUnpackTask != null) {
Task preBuildTask = project.tasks.getByName("pre" + variantName + "Build")
preBuildTask.dependsOn(appUnpackTask)
preBuildTask.dependsOn(eventAppCopyTask)
eventModule.tasks.getByName("preBuild").mustRunAfter appUnpackTask
eventModule.tasks.getByName("preBuild").mustRunAfter eventAppCopyTask
}
3.總結(jié)
上面就是這個(gè)插件的全部?jī)?nèi)容了,主要就是完成消息類型的自動(dòng)編譯,可以輔助組件化更好的解耦,組件開(kāi)發(fā)人員不需要去約定或者修改基礎(chǔ)庫(kù)才能達(dá)到數(shù)據(jù)傳輸?shù)哪康?。有個(gè)需要注意的地方就是目前只支持模塊是aar的包,jar包還不支持。
本文也是組件化的一部分內(nèi)容,后面會(huì)再更新一些在大廠中用到的組件化知識(shí),歡迎關(guān)注。
完。